feat: Token Exchange (RFC 8693) (#255)

This change implements OAuth2 Token Exchange in OP according to RFC 8693 (and client code)

Some implementation details:

- OP parses and verifies subject/actor tokens natively if they were issued by OP
- Third-party tokens verification is also possible by implementing additional storage interface
- Token exchange can issue only OP's native tokens (id_token, access_token and refresh_token) with static issuer
This commit is contained in:
Emil Bektimirov 2023-02-19 14:57:46 +01:00 committed by GitHub
parent 9291ca9908
commit 8e298791d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 961 additions and 59 deletions

View file

@ -74,6 +74,8 @@ func needsRefreshToken(tokenRequest TokenRequest, client AccessTokenClient) bool
switch req := tokenRequest.(type) {
case AuthRequest:
return strings.Contains(req.GetScopes(), oidc.ScopeOfflineAccess) && req.GetResponseType() == oidc.ResponseTypeCode && ValidateGrantType(client, oidc.GrantTypeRefreshToken)
case TokenExchangeRequest:
return req.GetRequestedTokenType() == oidc.RefreshTokenType
case RefreshTokenRequest:
return true
default:
@ -107,7 +109,23 @@ func CreateJWT(ctx context.Context, issuer string, tokenRequest TokenRequest, ex
claims := oidc.NewAccessTokenClaims(issuer, tokenRequest.GetSubject(), tokenRequest.GetAudience(), exp, id, client.GetID(), client.ClockSkew())
if client != nil {
restrictedScopes := client.RestrictAdditionalAccessTokenScopes()(tokenRequest.GetScopes())
privateClaims, err := storage.GetPrivateClaimsFromScopes(ctx, tokenRequest.GetSubject(), client.GetID(), removeUserinfoScopes(restrictedScopes))
var (
privateClaims map[string]interface{}
err error
)
tokenExchangeRequest, okReq := tokenRequest.(TokenExchangeRequest)
teStorage, okStorage := storage.(TokenExchangeStorage)
if okReq && okStorage {
privateClaims, err = teStorage.GetPrivateClaimsFromTokenExchangeRequest(
ctx,
tokenExchangeRequest,
)
} else {
privateClaims, err = storage.GetPrivateClaimsFromScopes(ctx, tokenRequest.GetSubject(), client.GetID(), removeUserinfoScopes(restrictedScopes))
}
if err != nil {
return "", err
}
@ -156,7 +174,17 @@ func CreateIDToken(ctx context.Context, issuer string, request IDTokenRequest, v
scopes = removeUserinfoScopes(scopes)
}
}
if len(scopes) > 0 {
tokenExchangeRequest, okReq := request.(TokenExchangeRequest)
teStorage, okStorage := storage.(TokenExchangeStorage)
if okReq && okStorage {
userInfo := oidc.NewUserInfo()
err := teStorage.SetUserinfoFromTokenExchangeRequest(ctx, userInfo, tokenExchangeRequest)
if err != nil {
return "", err
}
claims.SetUserinfo(userInfo)
} else if len(scopes) > 0 {
userInfo := oidc.NewUserInfo()
err := storage.SetUserinfoFromScopes(ctx, userInfo, request.GetSubject(), request.GetClientID(), scopes)
if err != nil {