Skip to content
Snippets Groups Projects
handlers.go 36.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Eric Chiang's avatar
    Eric Chiang committed
    
    
    	accessToken, err := s.newAccessToken(client.ID, claims, scopes, refresh.Nonce, refresh.ConnectorID)
    	if err != nil {
    		s.logger.Errorf("failed to create new access token: %v", err)
    		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
    		return
    	}
    
    
    	idToken, expiry, err := s.newIDToken(client.ID, claims, scopes, refresh.Nonce, accessToken, refresh.ConnectorID)
    
    Eric Chiang's avatar
    Eric Chiang committed
    	if err != nil {
    
    		s.logger.Errorf("failed to create ID token: %v", err)
    		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
    
    Eric Chiang's avatar
    Eric Chiang committed
    		return
    	}
    
    
    	newToken := &internal.RefreshToken{
    		RefreshId: refresh.ID,
    		Token:     storage.NewID(),
    	}
    	rawNewToken, err := internal.Marshal(newToken)
    	if err != nil {
    		s.logger.Errorf("failed to marshal refresh token: %v", err)
    
    		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
    
    Eric Chiang's avatar
    Eric Chiang committed
    		return
    	}
    
    	updater := func(old storage.RefreshToken) (storage.RefreshToken, error) {
    		if old.Token != refresh.Token {
    			return old, errors.New("refresh token claimed twice")
    		}
    		old.Token = newToken.Token
    		// Update the claims of the refresh token.
    		//
    		// UserID intentionally ignored for now.
    		old.Claims.Username = ident.Username
    		old.Claims.Email = ident.Email
    		old.Claims.EmailVerified = ident.EmailVerified
    		old.Claims.Groups = ident.Groups
    		old.ConnectorData = ident.ConnectorData
    
    		old.LastUsed = lastUsed
    
    
    	// Update LastUsed time stamp in refresh token reference object
    	// in offline session for the user.
    	if err := s.storage.UpdateOfflineSessions(refresh.Claims.UserID, refresh.ConnectorID, func(old storage.OfflineSessions) (storage.OfflineSessions, error) {
    		if old.Refresh[refresh.ClientID].ID != refresh.ID {
    			return old, errors.New("refresh token invalid")
    		}
    		old.Refresh[refresh.ClientID].LastUsed = lastUsed
    		return old, nil
    	}); err != nil {
    		s.logger.Errorf("failed to update offline session: %v", err)
    		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
    		return
    	}
    
    	// Update refresh token in the storage.
    
    	if err := s.storage.UpdateRefreshToken(refresh.ID, updater); err != nil {
    		s.logger.Errorf("failed to update refresh token: %v", err)
    
    		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
    
    Eric Chiang's avatar
    Eric Chiang committed
    		return
    	}
    
    	s.writeAccessToken(w, idToken, accessToken, rawNewToken, expiry)
    
    Eric Chiang's avatar
    Eric Chiang committed
    }
    
    
    func (s *Server) handleUserInfo(w http.ResponseWriter, r *http.Request) {
    
    	const prefix = "Bearer "
    
    	auth := r.Header.Get("authorization")
    	if len(auth) < len(prefix) || !strings.EqualFold(prefix, auth[:len(prefix)]) {
    		w.Header().Set("WWW-Authenticate", "Bearer")
    		s.tokenErrHelper(w, errAccessDenied, "Invalid bearer token.", http.StatusUnauthorized)
    
    	rawIDToken := auth[len(prefix):]
    
    	verifier := oidc.NewVerifier(s.issuerURL.String(), &storageKeySet{s.storage}, &oidc.Config{SkipClientIDCheck: true})
    	idToken, err := verifier.Verify(r.Context(), rawIDToken)
    
    	if err != nil {
    
    		s.tokenErrHelper(w, errAccessDenied, err.Error(), http.StatusForbidden)
    
    	var claims json.RawMessage
    	if err := idToken.Claims(&claims); err != nil {
    		s.tokenErrHelper(w, errServerError, err.Error(), http.StatusInternalServerError)
    		return
    
    	w.Header().Set("Content-Type", "application/json")
    	w.Write(claims)
    
    func (s *Server) writeAccessToken(w http.ResponseWriter, idToken, accessToken, refreshToken string, expiry time.Time) {
    
    Eric Chiang's avatar
    Eric Chiang committed
    	resp := struct {
    		AccessToken  string `json:"access_token"`
    		TokenType    string `json:"token_type"`
    		ExpiresIn    int    `json:"expires_in"`
    		RefreshToken string `json:"refresh_token,omitempty"`
    		IDToken      string `json:"id_token"`
    	}{
    
    		accessToken,
    
    Eric Chiang's avatar
    Eric Chiang committed
    		"bearer",
    
    		int(expiry.Sub(s.now()).Seconds()),
    
    Eric Chiang's avatar
    Eric Chiang committed
    		refreshToken,
    		idToken,
    	}
    	data, err := json.Marshal(resp)
    	if err != nil {
    
    		s.logger.Errorf("failed to marshal access token response: %v", err)
    		s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
    
    Eric Chiang's avatar
    Eric Chiang committed
    		return
    	}
    	w.Header().Set("Content-Type", "application/json")
    	w.Header().Set("Content-Length", strconv.Itoa(len(data)))
    	w.Write(data)
    }
    
    
    func (s *Server) renderError(w http.ResponseWriter, status int, description string) {
    
    	if err := s.templates.err(w, status, description); err != nil {
    
    		s.logger.Errorf("Server template error: %v", err)
    	}
    
    Eric Chiang's avatar
    Eric Chiang committed
    }
    
    
    func (s *Server) tokenErrHelper(w http.ResponseWriter, typ string, description string, statusCode int) {
    	if err := tokenErr(w, typ, description, statusCode); err != nil {
    
    		s.logger.Errorf("token error response: %v", err)
    
    
    // Check for username prompt override from connector. Defaults to "Username".
    func usernamePrompt(conn connector.PasswordConnector) string {
    	if attr := conn.Prompt(); attr != "" {
    		return attr
    	}
    	return "Username"
    }