This change adds Go 1.22 as a build target and drops support for Go 1.20 and older. The golang.org/x/exp/slog import is migrated to log/slog. Slog has been part of the Go standard library since Go 1.21. Therefore we are dropping support for older Go versions. This is in line of our support policy of "the latest two Go versions".
219 lines
5.1 KiB
Go
219 lines
5.1 KiB
Go
package storage
|
|
|
|
import (
|
|
"log/slog"
|
|
"time"
|
|
|
|
"golang.org/x/text/language"
|
|
|
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
|
"github.com/zitadel/oidc/v3/pkg/op"
|
|
)
|
|
|
|
const (
|
|
// CustomScope is an example for how to use custom scopes in this library
|
|
//(in this scenario, when requested, it will return a custom claim)
|
|
CustomScope = "custom_scope"
|
|
|
|
// CustomClaim is an example for how to return custom claims with this library
|
|
CustomClaim = "custom_claim"
|
|
|
|
// CustomScopeImpersonatePrefix is an example scope prefix for passing user id to impersonate using token exchage
|
|
CustomScopeImpersonatePrefix = "custom_scope:impersonate:"
|
|
)
|
|
|
|
type AuthRequest struct {
|
|
ID string
|
|
CreationDate time.Time
|
|
ApplicationID string
|
|
CallbackURI string
|
|
TransferState string
|
|
Prompt []string
|
|
UiLocales []language.Tag
|
|
LoginHint string
|
|
MaxAuthAge *time.Duration
|
|
UserID string
|
|
Scopes []string
|
|
ResponseType oidc.ResponseType
|
|
Nonce string
|
|
CodeChallenge *OIDCCodeChallenge
|
|
|
|
done bool
|
|
authTime time.Time
|
|
}
|
|
|
|
// LogValue allows you to define which fields will be logged.
|
|
// Implements the [slog.LogValuer]
|
|
func (a *AuthRequest) LogValue() slog.Value {
|
|
return slog.GroupValue(
|
|
slog.String("id", a.ID),
|
|
slog.Time("creation_date", a.CreationDate),
|
|
slog.Any("scopes", a.Scopes),
|
|
slog.String("response_type", string(a.ResponseType)),
|
|
slog.String("app_id", a.ApplicationID),
|
|
slog.String("callback_uri", a.CallbackURI),
|
|
)
|
|
}
|
|
|
|
func (a *AuthRequest) GetID() string {
|
|
return a.ID
|
|
}
|
|
|
|
func (a *AuthRequest) GetACR() string {
|
|
return "" // we won't handle acr in this example
|
|
}
|
|
|
|
func (a *AuthRequest) GetAMR() []string {
|
|
// this example only uses password for authentication
|
|
if a.done {
|
|
return []string{"pwd"}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *AuthRequest) GetAudience() []string {
|
|
return []string{a.ApplicationID} // this example will always just use the client_id as audience
|
|
}
|
|
|
|
func (a *AuthRequest) GetAuthTime() time.Time {
|
|
return a.authTime
|
|
}
|
|
|
|
func (a *AuthRequest) GetClientID() string {
|
|
return a.ApplicationID
|
|
}
|
|
|
|
func (a *AuthRequest) GetCodeChallenge() *oidc.CodeChallenge {
|
|
return CodeChallengeToOIDC(a.CodeChallenge)
|
|
}
|
|
|
|
func (a *AuthRequest) GetNonce() string {
|
|
return a.Nonce
|
|
}
|
|
|
|
func (a *AuthRequest) GetRedirectURI() string {
|
|
return a.CallbackURI
|
|
}
|
|
|
|
func (a *AuthRequest) GetResponseType() oidc.ResponseType {
|
|
return a.ResponseType
|
|
}
|
|
|
|
func (a *AuthRequest) GetResponseMode() oidc.ResponseMode {
|
|
return "" // we won't handle response mode in this example
|
|
}
|
|
|
|
func (a *AuthRequest) GetScopes() []string {
|
|
return a.Scopes
|
|
}
|
|
|
|
func (a *AuthRequest) GetState() string {
|
|
return a.TransferState
|
|
}
|
|
|
|
func (a *AuthRequest) GetSubject() string {
|
|
return a.UserID
|
|
}
|
|
|
|
func (a *AuthRequest) Done() bool {
|
|
return a.done
|
|
}
|
|
|
|
func PromptToInternal(oidcPrompt oidc.SpaceDelimitedArray) []string {
|
|
prompts := make([]string, len(oidcPrompt))
|
|
for _, oidcPrompt := range oidcPrompt {
|
|
switch oidcPrompt {
|
|
case oidc.PromptNone,
|
|
oidc.PromptLogin,
|
|
oidc.PromptConsent,
|
|
oidc.PromptSelectAccount:
|
|
prompts = append(prompts, oidcPrompt)
|
|
}
|
|
}
|
|
return prompts
|
|
}
|
|
|
|
func MaxAgeToInternal(maxAge *uint) *time.Duration {
|
|
if maxAge == nil {
|
|
return nil
|
|
}
|
|
dur := time.Duration(*maxAge) * time.Second
|
|
return &dur
|
|
}
|
|
|
|
func authRequestToInternal(authReq *oidc.AuthRequest, userID string) *AuthRequest {
|
|
return &AuthRequest{
|
|
CreationDate: time.Now(),
|
|
ApplicationID: authReq.ClientID,
|
|
CallbackURI: authReq.RedirectURI,
|
|
TransferState: authReq.State,
|
|
Prompt: PromptToInternal(authReq.Prompt),
|
|
UiLocales: authReq.UILocales,
|
|
LoginHint: authReq.LoginHint,
|
|
MaxAuthAge: MaxAgeToInternal(authReq.MaxAge),
|
|
UserID: userID,
|
|
Scopes: authReq.Scopes,
|
|
ResponseType: authReq.ResponseType,
|
|
Nonce: authReq.Nonce,
|
|
CodeChallenge: &OIDCCodeChallenge{
|
|
Challenge: authReq.CodeChallenge,
|
|
Method: string(authReq.CodeChallengeMethod),
|
|
},
|
|
}
|
|
}
|
|
|
|
type OIDCCodeChallenge struct {
|
|
Challenge string
|
|
Method string
|
|
}
|
|
|
|
func CodeChallengeToOIDC(challenge *OIDCCodeChallenge) *oidc.CodeChallenge {
|
|
if challenge == nil {
|
|
return nil
|
|
}
|
|
challengeMethod := oidc.CodeChallengeMethodPlain
|
|
if challenge.Method == "S256" {
|
|
challengeMethod = oidc.CodeChallengeMethodS256
|
|
}
|
|
return &oidc.CodeChallenge{
|
|
Challenge: challenge.Challenge,
|
|
Method: challengeMethod,
|
|
}
|
|
}
|
|
|
|
// RefreshTokenRequestFromBusiness will simply wrap the storage RefreshToken to implement the op.RefreshTokenRequest interface
|
|
func RefreshTokenRequestFromBusiness(token *RefreshToken) op.RefreshTokenRequest {
|
|
return &RefreshTokenRequest{token}
|
|
}
|
|
|
|
type RefreshTokenRequest struct {
|
|
*RefreshToken
|
|
}
|
|
|
|
func (r *RefreshTokenRequest) GetAMR() []string {
|
|
return r.AMR
|
|
}
|
|
|
|
func (r *RefreshTokenRequest) GetAudience() []string {
|
|
return r.Audience
|
|
}
|
|
|
|
func (r *RefreshTokenRequest) GetAuthTime() time.Time {
|
|
return r.AuthTime
|
|
}
|
|
|
|
func (r *RefreshTokenRequest) GetClientID() string {
|
|
return r.ApplicationID
|
|
}
|
|
|
|
func (r *RefreshTokenRequest) GetScopes() []string {
|
|
return r.Scopes
|
|
}
|
|
|
|
func (r *RefreshTokenRequest) GetSubject() string {
|
|
return r.UserID
|
|
}
|
|
|
|
func (r *RefreshTokenRequest) SetCurrentScopes(scopes []string) {
|
|
r.Scopes = scopes
|
|
}
|