func Test_Verify(t *testing.T) { // Grab an assertion from the mockmyid api assertion, err := mockmyid.RequestAssertion("*****@*****.**", tokenserver.DEFAULT_PERSONA_AUDIENCE) if err != nil { t.Error("Could not request assertion", err) } if len(assertion) == 0 { t.Error("Could not create assertion (it is zero length or not returned)") } // Run it through the verifier verifier, err := fxa.NewVerifier(tokenserver.DEFAULT_PERSONA_VERIFIER, tokenserver.DEFAULT_PERSONA_AUDIENCE) if err != nil { t.Error("Could not create a verifier") } response, err := verifier.VerifyAssertion(assertion) if err != nil { t.Error("Could not verify assertion") } if response.Status != "okay" { t.Errorf("Failed to verify assertion: %s / %s", response.Status, response.Reason) } }
func (c *tokenServerContext) SyncTokenHandler(w http.ResponseWriter, r *http.Request) { // Make sure we have a BrowserID Authorization header authorization := r.Header.Get("Authorization") if len(authorization) == 0 { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } tokens := strings.Split(authorization, " ") if len(tokens) != 2 { http.Error(w, "Unsupported authorization method", http.StatusUnauthorized) return } if tokens[0] != "BrowserID" { http.Error(w, "Unsupported authorization method", http.StatusUnauthorized) return } assertion := tokens[1] // Check if the client state is valid clientState := r.Header.Get("X-Client-State") if len(clientState) != 0 { if !clientIdValidator.MatchString(clientState) { http.Error(w, "Invalid X-Client-State", http.StatusInternalServerError) // TODO: JSON Error return } } // Verify the assertion verifier, err := fxa.NewVerifier(c.config.PersonaVerifier, c.config.PersonaAudience) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } personaResponse, err := verifier.VerifyAssertion(assertion) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if personaResponse.Status != "okay" { http.Error(w, "Invalid BrowserID assertion: "+personaResponse.Reason, http.StatusUnauthorized) return } // Grab the generation from the assertion. This conveniently // defaults to 0 if it is not present. generation := personaResponse.Claims.Generation // Load the user. Create if new and if signups are allowed. var user *User user, err = c.db.GetUser(personaResponse.Email) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if user == nil { if c.config.AllowNewUsers { user, err = c.db.AllocateUser(personaResponse.Email, generation, clientState) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } else { http.Error(w, "Invalid credentials", http.StatusUnauthorized) return } } // Deal with generation var newGeneration uint64 newClientState := "" if generation > user.Generation { newGeneration = generation } if clientState != user.ClientState { // Don't revert from some-client-state to no-client-state if len(clientState) == 0 { http.Error(w, "invalid-client-state", http.StatusUnauthorized) return } // Don't revert to a previous client-state if user.IsOldClientState(clientState) { http.Error(w, "invalid-client-state", http.StatusUnauthorized) return } // If the IdP has been sending generation numbers, then don't // update client-state without a change in generation number if user.Generation > 0 && newGeneration != 0 { http.Error(w, "invalid-client-state", http.StatusUnauthorized) return } newClientState = clientState } if newGeneration != 0 || len(newClientState) != 0 { user, err = c.db.UpdateUser(user.Email, newGeneration, newClientState) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } // Error out if this client is behind some previously-seen // client. This is done after the updates because some other, even // more up-to-date client may have raced with a concurrent update. if user.Generation > generation { http.Error(w, "invalid-generation", http.StatusUnauthorized) return } // Finally, create token and secret expires := time.Now().Unix() + c.config.TokenDuration payload := token.TokenPayload{ Uid: user.Uid, Node: c.config.StorageServerNode, Expires: expires, } token, err := token.NewToken([]byte(c.config.SharedSecret), payload) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // All done, build a response tokenServerResponse := &TokenServerResponse{ Id: token.Token, Key: token.DerivedSecret, Uid: user.Uid, ApiEndpoint: fmt.Sprintf("%s/1.5/%d", c.config.StorageServerNode, user.Uid), Duration: c.config.TokenDuration, } data, err := json.Marshal(tokenServerResponse) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Write(data) }