zitadel/internal/api/oidc/token_exchange_converter.go
Tim Möhlmann 6398349c24
feat(oidc): token exchange impersonation (#7516)
* add token exchange feature flag

* allow setting reason and actor to access tokens

* impersonation

* set token types and scopes in response

* upgrade oidc to working draft state

* fix tests

* audience and scope validation

* id toke and jwt as input

* return id tokens

* add grant type  token exchange to app config

* add integration tests

* check and deny actors in api calls

* fix instance setting tests by triggering projection on write and cleanup

* insert sleep statements again

* solve linting issues

* add translations

* pin oidc v3.15.0

* resolve comments, add event translation

* fix refreshtoken test

* use ValidateAuthReqScopes from oidc

* apparently the linter can't make up its mind

* persist actor thru refresh tokens and check in tests

* remove unneeded triggers
2024-03-20 10:18:46 +00:00

99 lines
2.4 KiB
Go

package oidc
import (
"time"
"github.com/zitadel/oidc/v3/pkg/oidc"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query"
)
type exchangeToken struct {
tokenType oidc.TokenType
userID string
issuer string
resourceOwner string
authTime time.Time
authMethods []domain.UserAuthMethodType
actor *domain.TokenActor
audience []string
scopes []string
}
func (et *exchangeToken) nestedActor() *domain.TokenActor {
return &domain.TokenActor{
Actor: et.actor,
UserID: et.userID,
Issuer: et.issuer,
}
}
func accessToExchangeToken(token *accessToken, issuer string) *exchangeToken {
return &exchangeToken{
tokenType: oidc.AccessTokenType,
userID: token.userID,
issuer: issuer,
resourceOwner: token.resourceOwner,
authMethods: token.authMethods,
actor: token.actor,
audience: token.audience,
scopes: token.scope,
}
}
func idTokenClaimsToExchangeToken(claims *oidc.IDTokenClaims, resourceOwner string) *exchangeToken {
return &exchangeToken{
tokenType: oidc.IDTokenType,
userID: claims.Subject,
issuer: claims.Issuer,
resourceOwner: resourceOwner,
authTime: claims.GetAuthTime(),
authMethods: AMRToAuthMethodTypes(claims.AuthenticationMethodsReferences),
actor: actorClaimsToDomain(claims.Actor),
audience: claims.Audience,
}
}
func actorClaimsToDomain(actor *oidc.ActorClaims) *domain.TokenActor {
if actor == nil {
return nil
}
return &domain.TokenActor{
Actor: actorClaimsToDomain(actor.Actor),
UserID: actor.Subject,
Issuer: actor.Issuer,
}
}
func actorDomainToClaims(actor *domain.TokenActor) *oidc.ActorClaims {
if actor == nil {
return nil
}
return &oidc.ActorClaims{
Actor: actorDomainToClaims(actor.Actor),
Subject: actor.UserID,
Issuer: actor.Issuer,
}
}
func jwtToExchangeToken(jwt *oidc.JWTTokenRequest, resourceOwner string) *exchangeToken {
return &exchangeToken{
tokenType: oidc.JWTTokenType,
userID: jwt.Subject,
issuer: jwt.Issuer,
resourceOwner: resourceOwner,
scopes: jwt.Scopes,
authTime: jwt.IssuedAt.AsTime(),
// audience omitted as we don't thrust audiences not signed by us
}
}
func userToExchangeToken(user *query.User) *exchangeToken {
return &exchangeToken{
tokenType: UserIDTokenType,
userID: user.ID,
resourceOwner: user.ResourceOwner,
}
}