Skip to content

Commit

Permalink
Fix Userinfo POST (#202)
Browse files Browse the repository at this point in the history
Make the endpoint unprotected and manually extract the access token from
the request body.
  • Loading branch information
spjmurray authored Feb 20, 2025
1 parent 96231d1 commit 2829851
Show file tree
Hide file tree
Showing 9 changed files with 371 additions and 303 deletions.
4 changes: 2 additions & 2 deletions charts/identity/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ description: A Helm chart for deploying Unikorn's IdP

type: application

version: v0.2.57
appVersion: v0.2.57
version: v0.2.58-rc1
appVersion: v0.2.58-rc1

icon: https://raw.githubusercontent.com/unikorn-cloud/assets/main/images/logos/dark-on-light/icon.png

Expand Down
11 changes: 8 additions & 3 deletions pkg/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,19 @@ func (h *Handler) GetOauth2V2Userinfo(w http.ResponseWriter, r *http.Request) {
}

func (h *Handler) PostOauth2V2Userinfo(w http.ResponseWriter, r *http.Request) {
info, err := authorization.FromContext(r.Context())
if err := r.ParseForm(); err != nil {
errors.HandleError(w, r, errors.OAuth2InvalidRequest("unable to parse form data").WithError(err))
return
}

userinfo, _, err := h.oauth2.GetUserinfo(r.Context(), r, r.Form.Get("access_token"))
if err != nil {
errors.HandleError(w, r, errors.OAuth2ServerError("userinfo is not set").WithError(err))
errors.HandleError(w, r, errors.OAuth2ServerError("access token is not set").WithError(err))
return
}

h.setUncacheable(w)
util.WriteJSONResponse(w, r, http.StatusOK, info.Userinfo)
util.WriteJSONResponse(w, r, http.StatusOK, userinfo)
}

func (h *Handler) GetOauth2V2Jwks(w http.ResponseWriter, r *http.Request) {
Expand Down
29 changes: 2 additions & 27 deletions pkg/middleware/openapi/local/authorizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package local
import (
"context"
"net/http"
"slices"
"strings"

"github.com/getkin/kin-openapi/openapi3filter"
Expand All @@ -31,8 +30,6 @@ import (
"github.com/unikorn-cloud/identity/pkg/openapi"
"github.com/unikorn-cloud/identity/pkg/rbac"
"github.com/unikorn-cloud/identity/pkg/util"

"k8s.io/utils/ptr"
)

// Authorizer provides OpenAPI based authorization middleware.
Expand Down Expand Up @@ -66,8 +63,6 @@ func getHTTPAuthenticationScheme(r *http.Request) (string, string, error) {
}

// authorizeOAuth2 checks APIs that require and oauth2 bearer token.
//
//nolint:cyclop
func (a *Authorizer) authorizeOAuth2(r *http.Request) (*authorization.Info, error) {
authorizationScheme, token, err := getHTTPAuthenticationScheme(r)
if err != nil {
Expand All @@ -78,31 +73,11 @@ func (a *Authorizer) authorizeOAuth2(r *http.Request) (*authorization.Info, erro
return nil, errors.OAuth2InvalidRequest("authorization scheme not allowed").WithValues("scheme", authorizationScheme)
}

verifyInfo := &oauth2.VerifyInfo{
Issuer: "https://" + r.Host,
Audience: r.Host,
Token: token,
}

// Check the token is from us, for us, and in date.
claims, err := a.authenticator.Verify(r.Context(), verifyInfo)
userinfo, claims, err := a.authenticator.GetUserinfo(r.Context(), r, token)
if err != nil {
return nil, errors.OAuth2AccessDenied("token validation failed").WithError(err)
}

userinfo := &openapi.Userinfo{
Sub: claims.Subject,
}

if claims.Custom != nil && slices.Contains(claims.Custom.Scope, "email") {
userinfo.Email = ptr.To(claims.Subject)
userinfo.EmailVerified = ptr.To(true)
return nil, err
}

// Need to expand the user information...
// if slices.Contains(claims.Custom.Scope, "profile") {
// }

info := &authorization.Info{
Token: token,
Userinfo: userinfo,
Expand Down
30 changes: 30 additions & 0 deletions pkg/oauth2/oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -1185,3 +1185,33 @@ func (a *Authenticator) Token(w http.ResponseWriter, r *http.Request) (*openapi.

return nil, errors.OAuth2InvalidRequest("token grant type is not supported")
}

// GetUserinfo does access token introspection.
func (a *Authenticator) GetUserinfo(ctx context.Context, r *http.Request, token string) (*openapi.Userinfo, *AccessTokenClaims, error) {
verifyInfo := &VerifyInfo{
Issuer: "https://" + r.Host,
Audience: r.Host,
Token: token,
}

// Check the token is from us, for us, and in date.
claims, err := a.Verify(ctx, verifyInfo)
if err != nil {
return nil, nil, errors.OAuth2AccessDenied("token validation failed").WithError(err)
}

userinfo := &openapi.Userinfo{
Sub: claims.Subject,
}

if claims.Custom != nil && slices.Contains(claims.Custom.Scope, "email") {
userinfo.Email = ptr.To(claims.Subject)
userinfo.EmailVerified = ptr.To(true)
}

// Need to expand the user information...
// if slices.Contains(claims.Custom.Scope, "profile") {
// }

return userinfo, claims, nil
}
61 changes: 49 additions & 12 deletions pkg/openapi/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions pkg/openapi/router.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 2829851

Please sign in to comment.