feat: add storage info to token responses
This commit is contained in:
parent
a8ef8de87b
commit
dcd3f46f02
8 changed files with 57 additions and 26 deletions
|
@ -5,10 +5,11 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
jose "github.com/go-jose/go-jose/v3"
|
"github.com/go-jose/go-jose/v3"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
"github.com/muhlemmer/gu"
|
"github.com/muhlemmer/gu"
|
||||||
|
|
||||||
"github.com/zitadel/oidc/v3/pkg/crypto"
|
"github.com/zitadel/oidc/v3/pkg/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -211,6 +212,7 @@ type AccessTokenResponse struct {
|
||||||
ExpiresIn uint64 `json:"expires_in,omitempty" schema:"expires_in,omitempty"`
|
ExpiresIn uint64 `json:"expires_in,omitempty" schema:"expires_in,omitempty"`
|
||||||
IDToken string `json:"id_token,omitempty" schema:"id_token,omitempty"`
|
IDToken string `json:"id_token,omitempty" schema:"id_token,omitempty"`
|
||||||
State string `json:"state,omitempty" schema:"state,omitempty"`
|
State string `json:"state,omitempty" schema:"state,omitempty"`
|
||||||
|
StorageInfo map[string]string `json:"storage_info,omitempty" schema:"storage_info,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JWTProfileAssertionClaims struct {
|
type JWTProfileAssertionClaims struct {
|
||||||
|
@ -352,4 +354,5 @@ type TokenExchangeResponse struct {
|
||||||
ExpiresIn uint64 `json:"expires_in,omitempty"`
|
ExpiresIn uint64 `json:"expires_in,omitempty"`
|
||||||
Scopes SpaceDelimitedArray `json:"scope,omitempty"`
|
Scopes SpaceDelimitedArray `json:"scope,omitempty"`
|
||||||
RefreshToken string `json:"refresh_token,omitempty"`
|
RefreshToken string `json:"refresh_token,omitempty"`
|
||||||
|
StorageInfo map[string]string `json:"storage_info,omitempty" schema:"storage_info,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,11 @@ import (
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
httphelper "github.com/zitadel/oidc/v3/pkg/http"
|
|
||||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
|
||||||
"github.com/zitadel/schema"
|
"github.com/zitadel/schema"
|
||||||
"golang.org/x/exp/slog"
|
"golang.org/x/exp/slog"
|
||||||
|
|
||||||
|
httphelper "github.com/zitadel/oidc/v3/pkg/http"
|
||||||
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RegisterServer registers an implementation of Server.
|
// RegisterServer registers an implementation of Server.
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
|
||||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -214,7 +215,11 @@ func (s *LegacyServer) CodeExchange(ctx context.Context, r *ClientRequest[oidc.A
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewResponse(resp), nil
|
ret := NewResponse(resp)
|
||||||
|
for k, v := range resp.StorageInfo {
|
||||||
|
ret.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LegacyServer) RefreshToken(ctx context.Context, r *ClientRequest[oidc.RefreshTokenRequest]) (*Response, error) {
|
func (s *LegacyServer) RefreshToken(ctx context.Context, r *ClientRequest[oidc.RefreshTokenRequest]) (*Response, error) {
|
||||||
|
@ -256,7 +261,11 @@ func (s *LegacyServer) JWTProfile(ctx context.Context, r *Request[oidc.JWTProfil
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewResponse(resp), nil
|
ret := NewResponse(resp)
|
||||||
|
for k, v := range resp.StorageInfo {
|
||||||
|
ret.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LegacyServer) TokenExchange(ctx context.Context, r *ClientRequest[oidc.TokenExchangeRequest]) (*Response, error) {
|
func (s *LegacyServer) TokenExchange(ctx context.Context, r *ClientRequest[oidc.TokenExchangeRequest]) (*Response, error) {
|
||||||
|
@ -271,7 +280,11 @@ func (s *LegacyServer) TokenExchange(ctx context.Context, r *ClientRequest[oidc.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewResponse(resp), nil
|
ret := NewResponse(resp)
|
||||||
|
for k, v := range resp.StorageInfo {
|
||||||
|
ret.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LegacyServer) ClientCredentialsExchange(ctx context.Context, r *ClientRequest[oidc.ClientCredentialsRequest]) (*Response, error) {
|
func (s *LegacyServer) ClientCredentialsExchange(ctx context.Context, r *ClientRequest[oidc.ClientCredentialsRequest]) (*Response, error) {
|
||||||
|
@ -287,7 +300,11 @@ func (s *LegacyServer) ClientCredentialsExchange(ctx context.Context, r *ClientR
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewResponse(resp), nil
|
ret := NewResponse(resp)
|
||||||
|
for k, v := range resp.StorageInfo {
|
||||||
|
ret.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LegacyServer) DeviceToken(ctx context.Context, r *ClientRequest[oidc.DeviceAccessTokenRequest]) (*Response, error) {
|
func (s *LegacyServer) DeviceToken(ctx context.Context, r *ClientRequest[oidc.DeviceAccessTokenRequest]) (*Response, error) {
|
||||||
|
@ -312,7 +329,11 @@ func (s *LegacyServer) DeviceToken(ctx context.Context, r *ClientRequest[oidc.De
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewResponse(resp), nil
|
ret := NewResponse(resp)
|
||||||
|
for k, v := range resp.StorageInfo {
|
||||||
|
ret.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LegacyServer) authenticateResourceClient(ctx context.Context, cc *ClientCredentials) (string, error) {
|
func (s *LegacyServer) authenticateResourceClient(ctx context.Context, cc *ClientCredentials) (string, error) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
jose "github.com/go-jose/go-jose/v3"
|
"github.com/go-jose/go-jose/v3"
|
||||||
|
|
||||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||||
)
|
)
|
||||||
|
@ -27,7 +27,7 @@ type AuthStorage interface {
|
||||||
// Grant: https://datatracker.ietf.org/doc/html/rfc7523#section-2.1
|
// Grant: https://datatracker.ietf.org/doc/html/rfc7523#section-2.1
|
||||||
//
|
//
|
||||||
// * TokenExchangeRequest as returned by ValidateTokenExchangeRequest
|
// * TokenExchangeRequest as returned by ValidateTokenExchangeRequest
|
||||||
CreateAccessToken(context.Context, TokenRequest) (accessTokenID string, expiration time.Time, err error)
|
CreateAccessToken(context.Context, TokenRequest) (accessTokenID string, expiration time.Time, storageInfo map[string]string, err error)
|
||||||
|
|
||||||
// The TokenRequest parameter of CreateAccessAndRefreshTokens can be any of:
|
// The TokenRequest parameter of CreateAccessAndRefreshTokens can be any of:
|
||||||
//
|
//
|
||||||
|
@ -40,7 +40,7 @@ type AuthStorage interface {
|
||||||
// registered the refresh_token grant type in advance
|
// registered the refresh_token grant type in advance
|
||||||
//
|
//
|
||||||
// * TokenExchangeRequest as returned by ValidateTokenExchangeRequest
|
// * TokenExchangeRequest as returned by ValidateTokenExchangeRequest
|
||||||
CreateAccessAndRefreshTokens(ctx context.Context, request TokenRequest, currentRefreshToken string) (accessTokenID string, newRefreshTokenID string, expiration time.Time, err error)
|
CreateAccessAndRefreshTokens(ctx context.Context, request TokenRequest, currentRefreshToken string) (accessTokenID string, newRefreshTokenID string, expiration time.Time, storageInfo map[string]string, err error)
|
||||||
TokenRequestByRefreshToken(ctx context.Context, refreshTokenID string) (RefreshTokenRequest, error)
|
TokenRequestByRefreshToken(ctx context.Context, refreshTokenID string) (RefreshTokenRequest, error)
|
||||||
|
|
||||||
TerminateSession(ctx context.Context, userID string, clientID string) error
|
TerminateSession(ctx context.Context, userID string, clientID string) error
|
||||||
|
|
|
@ -33,9 +33,10 @@ func CreateTokenResponse(ctx context.Context, request IDTokenRequest, client Cli
|
||||||
|
|
||||||
var accessToken, newRefreshToken string
|
var accessToken, newRefreshToken string
|
||||||
var validity time.Duration
|
var validity time.Duration
|
||||||
|
var storageInfo map[string]string
|
||||||
if createAccessToken {
|
if createAccessToken {
|
||||||
var err error
|
var err error
|
||||||
accessToken, newRefreshToken, validity, err = CreateAccessToken(ctx, request, client.AccessTokenType(), creator, client, refreshToken)
|
accessToken, newRefreshToken, validity, storageInfo, err = CreateAccessToken(ctx, request, client.AccessTokenType(), creator, client, refreshToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -65,14 +66,15 @@ func CreateTokenResponse(ctx context.Context, request IDTokenRequest, client Cli
|
||||||
TokenType: oidc.BearerToken,
|
TokenType: oidc.BearerToken,
|
||||||
ExpiresIn: exp,
|
ExpiresIn: exp,
|
||||||
State: state,
|
State: state,
|
||||||
|
StorageInfo: storageInfo,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTokens(ctx context.Context, tokenRequest TokenRequest, storage Storage, refreshToken string, client AccessTokenClient) (id, newRefreshToken string, exp time.Time, err error) {
|
func createTokens(ctx context.Context, tokenRequest TokenRequest, storage Storage, refreshToken string, client AccessTokenClient) (id, newRefreshToken string, exp time.Time, storageInfo map[string]string, err error) {
|
||||||
if needsRefreshToken(tokenRequest, client) {
|
if needsRefreshToken(tokenRequest, client) {
|
||||||
return storage.CreateAccessAndRefreshTokens(ctx, tokenRequest, refreshToken)
|
return storage.CreateAccessAndRefreshTokens(ctx, tokenRequest, refreshToken)
|
||||||
}
|
}
|
||||||
id, exp, err = storage.CreateAccessToken(ctx, tokenRequest)
|
id, exp, storageInfo, err = storage.CreateAccessToken(ctx, tokenRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,13 +91,13 @@ func needsRefreshToken(tokenRequest TokenRequest, client AccessTokenClient) bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateAccessToken(ctx context.Context, tokenRequest TokenRequest, accessTokenType AccessTokenType, creator TokenCreator, client AccessTokenClient, refreshToken string) (accessToken, newRefreshToken string, validity time.Duration, err error) {
|
func CreateAccessToken(ctx context.Context, tokenRequest TokenRequest, accessTokenType AccessTokenType, creator TokenCreator, client AccessTokenClient, refreshToken string) (accessToken, newRefreshToken string, validity time.Duration, storageInfo map[string]string, err error) {
|
||||||
ctx, span := tracer.Start(ctx, "CreateAccessToken")
|
ctx, span := tracer.Start(ctx, "CreateAccessToken")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
id, newRefreshToken, exp, err := createTokens(ctx, tokenRequest, creator.Storage(), refreshToken, client)
|
id, newRefreshToken, exp, storageInfo, err := createTokens(ctx, tokenRequest, creator.Storage(), refreshToken, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", 0, err
|
return "", "", 0, nil, err
|
||||||
}
|
}
|
||||||
var clockSkew time.Duration
|
var clockSkew time.Duration
|
||||||
if client != nil {
|
if client != nil {
|
||||||
|
|
|
@ -111,7 +111,7 @@ func CreateClientCredentialsTokenResponse(ctx context.Context, tokenRequest Toke
|
||||||
ctx, span := tracer.Start(ctx, "CreateClientCredentialsTokenResponse")
|
ctx, span := tracer.Start(ctx, "CreateClientCredentialsTokenResponse")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
accessToken, _, validity, err := CreateAccessToken(ctx, tokenRequest, client.AccessTokenType(), creator, client, "")
|
accessToken, _, validity, storageInfo, err := CreateAccessToken(ctx, tokenRequest, client.AccessTokenType(), creator, client, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -120,5 +120,6 @@ func CreateClientCredentialsTokenResponse(ctx context.Context, tokenRequest Toke
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
TokenType: oidc.BearerToken,
|
TokenType: oidc.BearerToken,
|
||||||
ExpiresIn: uint64(validity.Seconds()),
|
ExpiresIn: uint64(validity.Seconds()),
|
||||||
|
StorageInfo: storageInfo,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -363,11 +363,12 @@ func CreateTokenExchangeResponse(
|
||||||
var (
|
var (
|
||||||
token, refreshToken, tokenType string
|
token, refreshToken, tokenType string
|
||||||
validity time.Duration
|
validity time.Duration
|
||||||
|
storageInfo map[string]string
|
||||||
)
|
)
|
||||||
|
|
||||||
switch tokenExchangeRequest.GetRequestedTokenType() {
|
switch tokenExchangeRequest.GetRequestedTokenType() {
|
||||||
case oidc.AccessTokenType, oidc.RefreshTokenType:
|
case oidc.AccessTokenType, oidc.RefreshTokenType:
|
||||||
token, refreshToken, validity, err = CreateAccessToken(ctx, tokenExchangeRequest, client.AccessTokenType(), creator, client, "")
|
token, refreshToken, validity, storageInfo, err = CreateAccessToken(ctx, tokenExchangeRequest, client.AccessTokenType(), creator, client, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -396,6 +397,7 @@ func CreateTokenExchangeResponse(
|
||||||
ExpiresIn: exp,
|
ExpiresIn: exp,
|
||||||
RefreshToken: refreshToken,
|
RefreshToken: refreshToken,
|
||||||
Scopes: tokenExchangeRequest.GetScopes(),
|
Scopes: tokenExchangeRequest.GetScopes(),
|
||||||
|
StorageInfo: storageInfo,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ func CreateJWTTokenResponse(ctx context.Context, tokenRequest TokenRequest, crea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accessToken, _, validity, err := CreateAccessToken(ctx, tokenRequest, tokenType, creator, client, "")
|
accessToken, _, validity, storageInfo, err := CreateAccessToken(ctx, tokenRequest, tokenType, creator, client, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,7 @@ func CreateJWTTokenResponse(ctx context.Context, tokenRequest TokenRequest, crea
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
TokenType: oidc.BearerToken,
|
TokenType: oidc.BearerToken,
|
||||||
ExpiresIn: uint64(validity.Seconds()),
|
ExpiresIn: uint64(validity.Seconds()),
|
||||||
|
StorageInfo: storageInfo,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue