feat: add clock skew and option to put userinfo (profile, email, phone, address) into id_token
This commit is contained in:
parent
13b14734b9
commit
24120554e5
6 changed files with 61 additions and 13 deletions
|
@ -299,3 +299,11 @@ func (c *ConfClient) RestrictAdditionalAccessTokenScopes() func(scopes []string)
|
||||||
func (c *ConfClient) IsScopeAllowed(scope string) bool {
|
func (c *ConfClient) IsScopeAllowed(scope string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ConfClient) UserInfoInIDToken() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConfClient) ClockSkew() time.Duration {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
|
@ -48,8 +48,8 @@ func EmptyAccessTokenClaims() AccessTokenClaims {
|
||||||
return new(accessTokenClaims)
|
return new(accessTokenClaims)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAccessTokenClaims(issuer, subject string, audience []string, expiration time.Time, id, clientID string) AccessTokenClaims {
|
func NewAccessTokenClaims(issuer, subject string, audience []string, expiration time.Time, id, clientID string, skew time.Duration) AccessTokenClaims {
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC().Add(-skew)
|
||||||
if len(audience) == 0 {
|
if len(audience) == 0 {
|
||||||
audience = append(audience, clientID)
|
audience = append(audience, clientID)
|
||||||
}
|
}
|
||||||
|
@ -203,14 +203,14 @@ func EmptyIDTokenClaims() IDTokenClaims {
|
||||||
return new(idTokenClaims)
|
return new(idTokenClaims)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIDTokenClaims(issuer, subject string, audience []string, expiration, authTime time.Time, nonce string, acr string, amr []string, clientID string) IDTokenClaims {
|
func NewIDTokenClaims(issuer, subject string, audience []string, expiration, authTime time.Time, nonce string, acr string, amr []string, clientID string, skew time.Duration) IDTokenClaims {
|
||||||
audience = AppendClientIDToAudience(clientID, audience)
|
audience = AppendClientIDToAudience(clientID, audience)
|
||||||
return &idTokenClaims{
|
return &idTokenClaims{
|
||||||
Issuer: issuer,
|
Issuer: issuer,
|
||||||
Audience: audience,
|
Audience: audience,
|
||||||
Expiration: Time(expiration),
|
Expiration: Time(expiration),
|
||||||
IssuedAt: Time(time.Now().UTC()),
|
IssuedAt: Time(time.Now().UTC().Add(-skew)),
|
||||||
AuthTime: Time(authTime),
|
AuthTime: Time(authTime.Add(-skew)),
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
AuthenticationContextClassReference: acr,
|
AuthenticationContextClassReference: acr,
|
||||||
AuthenticationMethodsReferences: amr,
|
AuthenticationMethodsReferences: amr,
|
||||||
|
|
|
@ -37,6 +37,8 @@ type Client interface {
|
||||||
RestrictAdditionalIdTokenScopes() func(scopes []string) []string
|
RestrictAdditionalIdTokenScopes() func(scopes []string) []string
|
||||||
RestrictAdditionalAccessTokenScopes() func(scopes []string) []string
|
RestrictAdditionalAccessTokenScopes() func(scopes []string) []string
|
||||||
IsScopeAllowed(scope string) bool
|
IsScopeAllowed(scope string) bool
|
||||||
|
UserInfoInIDToken() bool
|
||||||
|
ClockSkew() time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func ContainsResponseType(types []oidc.ResponseType, responseType oidc.ResponseType) bool {
|
func ContainsResponseType(types []oidc.ResponseType, responseType oidc.ResponseType) bool {
|
||||||
|
|
|
@ -77,6 +77,20 @@ func (mr *MockClientMockRecorder) AuthMethod() *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthMethod", reflect.TypeOf((*MockClient)(nil).AuthMethod))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthMethod", reflect.TypeOf((*MockClient)(nil).AuthMethod))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClockSkew mocks base method
|
||||||
|
func (m *MockClient) ClockSkew() time.Duration {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ClockSkew")
|
||||||
|
ret0, _ := ret[0].(time.Duration)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClockSkew indicates an expected call of ClockSkew
|
||||||
|
func (mr *MockClientMockRecorder) ClockSkew() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClockSkew", reflect.TypeOf((*MockClient)(nil).ClockSkew))
|
||||||
|
}
|
||||||
|
|
||||||
// DevMode mocks base method
|
// DevMode mocks base method
|
||||||
func (m *MockClient) DevMode() bool {
|
func (m *MockClient) DevMode() bool {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
@ -216,3 +230,17 @@ func (mr *MockClientMockRecorder) RestrictAdditionalIdTokenScopes() *gomock.Call
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestrictAdditionalIdTokenScopes", reflect.TypeOf((*MockClient)(nil).RestrictAdditionalIdTokenScopes))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestrictAdditionalIdTokenScopes", reflect.TypeOf((*MockClient)(nil).RestrictAdditionalIdTokenScopes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserInfoInIDToken mocks base method
|
||||||
|
func (m *MockClient) UserInfoInIDToken() bool {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "UserInfoInIDToken")
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserInfoInIDToken indicates an expected call of UserInfoInIDToken
|
||||||
|
func (mr *MockClientMockRecorder) UserInfoInIDToken() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserInfoInIDToken", reflect.TypeOf((*MockClient)(nil).UserInfoInIDToken))
|
||||||
|
}
|
||||||
|
|
|
@ -184,3 +184,11 @@ func (c *ConfClient) RestrictAdditionalAccessTokenScopes() func(scopes []string)
|
||||||
func (c *ConfClient) IsScopeAllowed(scope string) bool {
|
func (c *ConfClient) IsScopeAllowed(scope string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ConfClient) UserInfoInIDToken() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConfClient) ClockSkew() time.Duration {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ func CreateTokenResponse(ctx context.Context, authReq AuthRequest, client Client
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
idToken, err := CreateIDToken(ctx, creator.Issuer(), authReq, client.IDTokenLifetime(), accessToken, code, creator.Storage(), creator.Signer(), client.RestrictAdditionalIdTokenScopes())
|
idToken, err := CreateIDToken(ctx, creator.Issuer(), authReq, client.IDTokenLifetime(), accessToken, code, creator.Storage(), creator.Signer(), client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func CreateAccessToken(ctx context.Context, tokenRequest TokenRequest, accessTok
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
validity = exp.Sub(time.Now().UTC())
|
validity = exp.Add(client.ClockSkew()).Sub(time.Now().UTC())
|
||||||
if accessTokenType == AccessTokenTypeJWT {
|
if accessTokenType == AccessTokenTypeJWT {
|
||||||
token, err = CreateJWT(ctx, creator.Issuer(), tokenRequest, exp, id, creator.Signer(), client, creator.Storage())
|
token, err = CreateJWT(ctx, creator.Issuer(), tokenRequest, exp, id, creator.Signer(), client, creator.Storage())
|
||||||
return
|
return
|
||||||
|
@ -83,7 +83,7 @@ func CreateBearerToken(tokenID, subject string, crypto Crypto) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateJWT(ctx context.Context, issuer string, tokenRequest TokenRequest, exp time.Time, id string, signer Signer, client Client, storage Storage) (string, error) {
|
func CreateJWT(ctx context.Context, issuer string, tokenRequest TokenRequest, exp time.Time, id string, signer Signer, client Client, storage Storage) (string, error) {
|
||||||
claims := oidc.NewAccessTokenClaims(issuer, tokenRequest.GetSubject(), tokenRequest.GetAudience(), exp, id, client.GetID())
|
claims := oidc.NewAccessTokenClaims(issuer, tokenRequest.GetSubject(), tokenRequest.GetAudience(), exp, id, client.GetID(), client.ClockSkew())
|
||||||
if client != nil {
|
if client != nil {
|
||||||
restrictedScopes := client.RestrictAdditionalAccessTokenScopes()(tokenRequest.GetScopes())
|
restrictedScopes := client.RestrictAdditionalAccessTokenScopes()(tokenRequest.GetScopes())
|
||||||
privateClaims, err := storage.GetPrivateClaimsFromScopes(ctx, tokenRequest.GetSubject(), client.GetID(), removeUserinfoScopes(restrictedScopes))
|
privateClaims, err := storage.GetPrivateClaimsFromScopes(ctx, tokenRequest.GetSubject(), client.GetID(), removeUserinfoScopes(restrictedScopes))
|
||||||
|
@ -95,18 +95,20 @@ func CreateJWT(ctx context.Context, issuer string, tokenRequest TokenRequest, ex
|
||||||
return utils.Sign(claims, signer.Signer())
|
return utils.Sign(claims, signer.Signer())
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateIDToken(ctx context.Context, issuer string, authReq AuthRequest, validity time.Duration, accessToken, code string, storage Storage, signer Signer, restictAdditionalScopesFunc func([]string) []string) (string, error) {
|
func CreateIDToken(ctx context.Context, issuer string, authReq AuthRequest, validity time.Duration, accessToken, code string, storage Storage, signer Signer, client Client) (string, error) {
|
||||||
exp := time.Now().UTC().Add(validity)
|
exp := time.Now().UTC().Add(client.ClockSkew()).Add(validity)
|
||||||
claims := oidc.NewIDTokenClaims(issuer, authReq.GetSubject(), authReq.GetAudience(), exp, authReq.GetAuthTime(), authReq.GetNonce(), authReq.GetACR(), authReq.GetAMR(), authReq.GetClientID())
|
claims := oidc.NewIDTokenClaims(issuer, authReq.GetSubject(), authReq.GetAudience(), exp, authReq.GetAuthTime(), authReq.GetNonce(), authReq.GetACR(), authReq.GetAMR(), authReq.GetClientID(), client.ClockSkew())
|
||||||
scopes := restictAdditionalScopesFunc(authReq.GetScopes())
|
scopes := client.RestrictAdditionalIdTokenScopes()(authReq.GetScopes())
|
||||||
if accessToken != "" {
|
if accessToken != "" {
|
||||||
atHash, err := oidc.ClaimHash(accessToken, signer.SignatureAlgorithm())
|
atHash, err := oidc.ClaimHash(accessToken, signer.SignatureAlgorithm())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
claims.SetAccessTokenHash(atHash)
|
claims.SetAccessTokenHash(atHash)
|
||||||
|
if !client.UserInfoInIDToken() {
|
||||||
scopes = removeUserinfoScopes(scopes)
|
scopes = removeUserinfoScopes(scopes)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if len(scopes) > 0 {
|
if len(scopes) > 0 {
|
||||||
userInfo, err := storage.GetUserinfoFromScopes(ctx, authReq.GetSubject(), authReq.GetClientID(), scopes)
|
userInfo, err := storage.GetUserinfoFromScopes(ctx, authReq.GetSubject(), authReq.GetClientID(), scopes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue