refactor: use struct types for claim related types
BREAKING change. The following types are changed from interface to struct type: - AccessTokenClaims - IDTokenClaims - IntrospectionResponse - UserInfo and related types. The following methods of OPStorage now take a pointer to a struct type, instead of an interface: - SetUserinfoFromScopes - SetUserinfoFromToken - SetIntrospectionFromToken The following functions are now generic, so that type-safe extension of Claims is now possible: - op.VerifyIDTokenHint - op.VerifyAccessToken - rp.VerifyTokens - rp.VerifyIDToken
This commit is contained in:
parent
11682a2cc8
commit
85bd99873d
40 changed files with 857 additions and 1291 deletions
|
@ -371,7 +371,7 @@ func ValidateAuthReqIDTokenHint(ctx context.Context, idTokenHint string, verifie
|
|||
if idTokenHint == "" {
|
||||
return "", nil
|
||||
}
|
||||
claims, err := VerifyIDTokenHint(ctx, idTokenHint, verifier)
|
||||
claims, err := VerifyIDTokenHint[*oidc.TokenClaims](ctx, idTokenHint, verifier)
|
||||
if err != nil {
|
||||
return "", oidc.ErrLoginRequired().WithDescription("The id_token_hint is invalid. " +
|
||||
"If you have any questions, you may contact the administrator of the application.")
|
||||
|
|
|
@ -263,7 +263,7 @@ func (mr *MockStorageMockRecorder) SaveAuthCode(arg0, arg1, arg2 interface{}) *g
|
|||
}
|
||||
|
||||
// SetIntrospectionFromToken mocks base method.
|
||||
func (m *MockStorage) SetIntrospectionFromToken(arg0 context.Context, arg1 oidc.IntrospectionResponse, arg2, arg3, arg4 string) error {
|
||||
func (m *MockStorage) SetIntrospectionFromToken(arg0 context.Context, arg1 *oidc.IntrospectionResponse, arg2, arg3, arg4 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetIntrospectionFromToken", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(error)
|
||||
|
@ -277,7 +277,7 @@ func (mr *MockStorageMockRecorder) SetIntrospectionFromToken(arg0, arg1, arg2, a
|
|||
}
|
||||
|
||||
// SetUserinfoFromScopes mocks base method.
|
||||
func (m *MockStorage) SetUserinfoFromScopes(arg0 context.Context, arg1 oidc.UserInfoSetter, arg2, arg3 string, arg4 []string) error {
|
||||
func (m *MockStorage) SetUserinfoFromScopes(arg0 context.Context, arg1 *oidc.UserInfo, arg2, arg3 string, arg4 []string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetUserinfoFromScopes", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(error)
|
||||
|
@ -291,7 +291,7 @@ func (mr *MockStorageMockRecorder) SetUserinfoFromScopes(arg0, arg1, arg2, arg3,
|
|||
}
|
||||
|
||||
// SetUserinfoFromToken mocks base method.
|
||||
func (m *MockStorage) SetUserinfoFromToken(arg0 context.Context, arg1 oidc.UserInfoSetter, arg2, arg3, arg4 string) error {
|
||||
func (m *MockStorage) SetUserinfoFromToken(arg0 context.Context, arg1 *oidc.UserInfo, arg2, arg3, arg4 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetUserinfoFromToken", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(error)
|
||||
|
|
|
@ -59,7 +59,7 @@ func ValidateEndSessionRequest(ctx context.Context, req *oidc.EndSessionRequest,
|
|||
RedirectURI: ender.DefaultLogoutRedirectURI(),
|
||||
}
|
||||
if req.IdTokenHint != "" {
|
||||
claims, err := VerifyIDTokenHint(ctx, req.IdTokenHint, ender.IDTokenHintVerifier(ctx))
|
||||
claims, err := VerifyIDTokenHint[*oidc.TokenClaims](ctx, req.IdTokenHint, ender.IDTokenHintVerifier(ctx))
|
||||
if err != nil {
|
||||
return nil, oidc.ErrInvalidRequest().WithDescription("id_token_hint invalid").WithParent(err)
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ type TokenExchangeStorage interface {
|
|||
|
||||
// SetUserinfoFromTokenExchangeRequest will be called during id token creation.
|
||||
// Claims evaluation can be based on all validated request data available, including: scopes, resource, audience, etc.
|
||||
SetUserinfoFromTokenExchangeRequest(ctx context.Context, userinfo oidc.UserInfoSetter, request TokenExchangeRequest) error
|
||||
SetUserinfoFromTokenExchangeRequest(ctx context.Context, userinfo *oidc.UserInfo, request TokenExchangeRequest) error
|
||||
}
|
||||
|
||||
// TokenExchangeTokensVerifierStorage is an optional interface used in token exchange process to verify tokens
|
||||
|
@ -111,9 +111,9 @@ var ErrInvalidRefreshToken = errors.New("invalid_refresh_token")
|
|||
type OPStorage interface {
|
||||
GetClientByClientID(ctx context.Context, clientID string) (Client, error)
|
||||
AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string) error
|
||||
SetUserinfoFromScopes(ctx context.Context, userinfo oidc.UserInfoSetter, userID, clientID string, scopes []string) error
|
||||
SetUserinfoFromToken(ctx context.Context, userinfo oidc.UserInfoSetter, tokenID, subject, origin string) error
|
||||
SetIntrospectionFromToken(ctx context.Context, userinfo oidc.IntrospectionResponse, tokenID, subject, clientID string) error
|
||||
SetUserinfoFromScopes(ctx context.Context, userinfo *oidc.UserInfo, userID, clientID string, scopes []string) error
|
||||
SetUserinfoFromToken(ctx context.Context, userinfo *oidc.UserInfo, tokenID, subject, origin string) error
|
||||
SetIntrospectionFromToken(ctx context.Context, userinfo *oidc.IntrospectionResponse, tokenID, subject, clientID string) error
|
||||
GetPrivateClaimsFromScopes(ctx context.Context, userID, clientID string, scopes []string) (map[string]interface{}, error)
|
||||
GetKeyByIDAndClientID(ctx context.Context, keyID, clientID string) (*jose.JSONWebKey, error)
|
||||
ValidateJWTProfileScopes(ctx context.Context, userID string, scopes []string) ([]string, error)
|
||||
|
|
|
@ -129,7 +129,7 @@ func CreateJWT(ctx context.Context, issuer string, tokenRequest TokenRequest, ex
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
claims.SetPrivateClaims(privateClaims)
|
||||
claims.Claims = privateClaims
|
||||
}
|
||||
signingKey, err := storage.SigningKey(ctx)
|
||||
if err != nil {
|
||||
|
@ -169,7 +169,7 @@ func CreateIDToken(ctx context.Context, issuer string, request IDTokenRequest, v
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
claims.SetAccessTokenHash(atHash)
|
||||
claims.AccessTokenHash = atHash
|
||||
if !client.IDTokenUserinfoClaimsAssertion() {
|
||||
scopes = removeUserinfoScopes(scopes)
|
||||
}
|
||||
|
@ -178,26 +178,26 @@ func CreateIDToken(ctx context.Context, issuer string, request IDTokenRequest, v
|
|||
tokenExchangeRequest, okReq := request.(TokenExchangeRequest)
|
||||
teStorage, okStorage := storage.(TokenExchangeStorage)
|
||||
if okReq && okStorage {
|
||||
userInfo := oidc.NewUserInfo()
|
||||
userInfo := new(oidc.UserInfo)
|
||||
err := teStorage.SetUserinfoFromTokenExchangeRequest(ctx, userInfo, tokenExchangeRequest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
claims.SetUserinfo(userInfo)
|
||||
claims.SetUserInfo(userInfo)
|
||||
} else if len(scopes) > 0 {
|
||||
userInfo := oidc.NewUserInfo()
|
||||
userInfo := new(oidc.UserInfo)
|
||||
err := storage.SetUserinfoFromScopes(ctx, userInfo, request.GetSubject(), request.GetClientID(), scopes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
claims.SetUserinfo(userInfo)
|
||||
claims.SetUserInfo(userInfo)
|
||||
}
|
||||
if code != "" {
|
||||
codeHash, err := oidc.ClaimHash(code, signingKey.SignatureAlgorithm())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
claims.SetCodeHash(codeHash)
|
||||
claims.CodeHash = codeHash
|
||||
}
|
||||
signer, err := SignerFromKey(signingKey)
|
||||
if err != nil {
|
||||
|
|
|
@ -280,9 +280,9 @@ func GetTokenIDAndSubjectFromToken(
|
|||
) (tokenIDOrToken, subject string, claims map[string]interface{}, ok bool) {
|
||||
switch tokenType {
|
||||
case oidc.AccessTokenType:
|
||||
var accessTokenClaims oidc.AccessTokenClaims
|
||||
var accessTokenClaims *oidc.AccessTokenClaims
|
||||
tokenIDOrToken, subject, accessTokenClaims, ok = getTokenIDAndClaims(ctx, exchanger, token)
|
||||
claims = accessTokenClaims.GetClaims()
|
||||
claims = accessTokenClaims.Claims
|
||||
case oidc.RefreshTokenType:
|
||||
refreshTokenRequest, err := exchanger.Storage().TokenRequestByRefreshToken(ctx, token)
|
||||
if err != nil {
|
||||
|
@ -291,12 +291,12 @@ func GetTokenIDAndSubjectFromToken(
|
|||
|
||||
tokenIDOrToken, subject, ok = token, refreshTokenRequest.GetSubject(), true
|
||||
case oidc.IDTokenType:
|
||||
idTokenClaims, err := VerifyIDTokenHint(ctx, token, exchanger.IDTokenHintVerifier(ctx))
|
||||
idTokenClaims, err := VerifyIDTokenHint[*oidc.IDTokenClaims](ctx, token, exchanger.IDTokenHintVerifier(ctx))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
tokenIDOrToken, subject, claims, ok = token, idTokenClaims.GetSubject(), idTokenClaims.GetClaims(), true
|
||||
tokenIDOrToken, subject, claims, ok = token, idTokenClaims.Subject, idTokenClaims.Claims, true
|
||||
}
|
||||
|
||||
if !ok {
|
||||
|
@ -380,7 +380,7 @@ func CreateTokenExchangeResponse(
|
|||
}, nil
|
||||
}
|
||||
|
||||
func getTokenIDAndClaims(ctx context.Context, userinfoProvider UserinfoProvider, accessToken string) (string, string, oidc.AccessTokenClaims, bool) {
|
||||
func getTokenIDAndClaims(ctx context.Context, userinfoProvider UserinfoProvider, accessToken string) (string, string, *oidc.AccessTokenClaims, bool) {
|
||||
tokenIDSubject, err := userinfoProvider.Crypto().Decrypt(accessToken)
|
||||
if err == nil {
|
||||
splitToken := strings.Split(tokenIDSubject, ":")
|
||||
|
@ -390,10 +390,10 @@ func getTokenIDAndClaims(ctx context.Context, userinfoProvider UserinfoProvider,
|
|||
|
||||
return splitToken[0], splitToken[1], nil, true
|
||||
}
|
||||
accessTokenClaims, err := VerifyAccessToken(ctx, accessToken, userinfoProvider.AccessTokenVerifier(ctx))
|
||||
accessTokenClaims, err := VerifyAccessToken[*oidc.AccessTokenClaims](ctx, accessToken, userinfoProvider.AccessTokenVerifier(ctx))
|
||||
if err != nil {
|
||||
return "", "", nil, false
|
||||
}
|
||||
|
||||
return accessTokenClaims.GetTokenID(), accessTokenClaims.GetSubject(), accessTokenClaims, true
|
||||
return accessTokenClaims.JWTID, accessTokenClaims.Subject, accessTokenClaims, true
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ func introspectionHandler(introspector Introspector) func(http.ResponseWriter, *
|
|||
}
|
||||
|
||||
func Introspect(w http.ResponseWriter, r *http.Request, introspector Introspector) {
|
||||
response := oidc.NewIntrospectionResponse()
|
||||
response := new(oidc.IntrospectionResponse)
|
||||
token, clientID, err := ParseTokenIntrospectionRequest(r, introspector)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
|
@ -44,7 +44,7 @@ func Introspect(w http.ResponseWriter, r *http.Request, introspector Introspecto
|
|||
httphelper.MarshalJSON(w, response)
|
||||
return
|
||||
}
|
||||
response.SetActive(true)
|
||||
response.Active = true
|
||||
httphelper.MarshalJSON(w, response)
|
||||
}
|
||||
|
||||
|
|
|
@ -151,9 +151,9 @@ func getTokenIDAndSubjectForRevocation(ctx context.Context, userinfoProvider Use
|
|||
}
|
||||
return splitToken[0], splitToken[1], true
|
||||
}
|
||||
accessTokenClaims, err := VerifyAccessToken(ctx, accessToken, userinfoProvider.AccessTokenVerifier(ctx))
|
||||
accessTokenClaims, err := VerifyAccessToken[*oidc.AccessTokenClaims](ctx, accessToken, userinfoProvider.AccessTokenVerifier(ctx))
|
||||
if err != nil {
|
||||
return "", "", false
|
||||
}
|
||||
return accessTokenClaims.GetTokenID(), accessTokenClaims.GetSubject(), true
|
||||
return accessTokenClaims.JWTID, accessTokenClaims.Subject, true
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ func Userinfo(w http.ResponseWriter, r *http.Request, userinfoProvider UserinfoP
|
|||
http.Error(w, "access token invalid", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
info := oidc.NewUserInfo()
|
||||
info := new(oidc.UserInfo)
|
||||
err = userinfoProvider.Storage().SetUserinfoFromToken(r.Context(), info, tokenID, subject, r.Header.Get("origin"))
|
||||
if err != nil {
|
||||
httphelper.MarshalJSONWithStatus(w, err, http.StatusForbidden)
|
||||
|
@ -81,9 +81,9 @@ func getTokenIDAndSubject(ctx context.Context, userinfoProvider UserinfoProvider
|
|||
}
|
||||
return splitToken[0], splitToken[1], true
|
||||
}
|
||||
accessTokenClaims, err := VerifyAccessToken(ctx, accessToken, userinfoProvider.AccessTokenVerifier(ctx))
|
||||
accessTokenClaims, err := VerifyAccessToken[*oidc.AccessTokenClaims](ctx, accessToken, userinfoProvider.AccessTokenVerifier(ctx))
|
||||
if err != nil {
|
||||
return "", "", false
|
||||
}
|
||||
return accessTokenClaims.GetTokenID(), accessTokenClaims.GetSubject(), true
|
||||
return accessTokenClaims.JWTID, accessTokenClaims.Subject, true
|
||||
}
|
||||
|
|
|
@ -68,28 +68,28 @@ func NewAccessTokenVerifier(issuer string, keySet oidc.KeySet, opts ...AccessTok
|
|||
}
|
||||
|
||||
// VerifyAccessToken validates the access token (issuer, signature and expiration)
|
||||
func VerifyAccessToken(ctx context.Context, token string, v AccessTokenVerifier) (oidc.AccessTokenClaims, error) {
|
||||
claims := oidc.EmptyAccessTokenClaims()
|
||||
func VerifyAccessToken[C oidc.Claims](ctx context.Context, token string, v AccessTokenVerifier) (claims C, err error) {
|
||||
var nilClaims C
|
||||
|
||||
decrypted, err := oidc.DecryptToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
payload, err := oidc.ParseToken(decrypted, claims)
|
||||
payload, err := oidc.ParseToken(decrypted, &claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
|
||||
if err := oidc.CheckIssuer(claims, v.Issuer()); err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
|
||||
if err = oidc.CheckSignature(ctx, decrypted, payload, claims, v.SupportedSignAlgs(), v.KeySet()); err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
|
||||
if err = oidc.CheckExpiration(claims, v.Offset()); err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
|
|
|
@ -74,40 +74,40 @@ func NewIDTokenHintVerifier(issuer string, keySet oidc.KeySet, opts ...IDTokenHi
|
|||
|
||||
// VerifyIDTokenHint validates the id token according to
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||
func VerifyIDTokenHint(ctx context.Context, token string, v IDTokenHintVerifier) (oidc.IDTokenClaims, error) {
|
||||
claims := oidc.EmptyIDTokenClaims()
|
||||
func VerifyIDTokenHint[C oidc.Claims](ctx context.Context, token string, v IDTokenHintVerifier) (claims C, err error) {
|
||||
var nilClaims C
|
||||
|
||||
decrypted, err := oidc.DecryptToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
payload, err := oidc.ParseToken(decrypted, claims)
|
||||
payload, err := oidc.ParseToken(decrypted, &claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
|
||||
if err := oidc.CheckIssuer(claims, v.Issuer()); err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
|
||||
if err = oidc.CheckSignature(ctx, decrypted, payload, claims, v.SupportedSignAlgs(), v.KeySet()); err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
|
||||
if err = oidc.CheckExpiration(claims, v.Offset()); err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
|
||||
if err = oidc.CheckIssuedAt(claims, v.MaxAgeIAT(), v.Offset()); err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
|
||||
if err = oidc.CheckAuthorizationContextClassReference(claims, v.ACR()); err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
|
||||
if err = oidc.CheckAuthTime(claims, v.MaxAge()); err != nil {
|
||||
return nil, err
|
||||
return nilClaims, err
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue