Implement handling OAuth2 client token lifespans. (#145)

This commit is contained in:
David Wobrock 2024-06-24 13:52:50 +02:00 committed by GitHub
parent 8029e019dd
commit 8f679ba89a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 215 additions and 34 deletions

View File

@ -51,6 +51,69 @@ type HydraAdmin struct {
ForwardedProto string `json:"forwardedProto,omitempty"`
}
// TokenLifespans defines the desired token durations by grant type for OAuth2Client
type TokenLifespans struct {
// +kubebuilder:validation:Pattern=[0-9]+(ns|us|ms|s|m|h)
//
// AuthorizationCodeGrantAccessTokenLifespan is the access token lifespan
// issued on an authorization_code grant.
AuthorizationCodeGrantAccessTokenLifespan string `json:"authorization_code_grant_access_token_lifespan,omitempty"`
// +kubebuilder:validation:Pattern=[0-9]+(ns|us|ms|s|m|h)
//
// AuthorizationCodeGrantIdTokenLifespan is the id token lifespan
// issued on an authorization_code grant.
AuthorizationCodeGrantIdTokenLifespan string `json:"authorization_code_grant_id_token_lifespan,omitempty"`
// +kubebuilder:validation:Pattern=[0-9]+(ns|us|ms|s|m|h)
//
// AuthorizationCodeGrantRefreshTokenLifespan is the refresh token lifespan
// issued on an authorization_code grant.
AuthorizationCodeGrantRefreshTokenLifespan string `json:"authorization_code_grant_refresh_token_lifespan,omitempty"`
// +kubebuilder:validation:Pattern=[0-9]+(ns|us|ms|s|m|h)
//
// AuthorizationCodeGrantRefreshTokenLifespan is the access token lifespan
// issued on a client_credentials grant.
ClientCredentialsGrantAccessTokenLifespan string `json:"client_credentials_grant_access_token_lifespan,omitempty"`
// +kubebuilder:validation:Pattern=[0-9]+(ns|us|ms|s|m|h)
//
// ImplicitGrantAccessTokenLifespan is the access token lifespan
// issued on an implicit grant.
ImplicitGrantAccessTokenLifespan string `json:"implicit_grant_access_token_lifespan,omitempty"`
// +kubebuilder:validation:Pattern=[0-9]+(ns|us|ms|s|m|h)
//
// ImplicitGrantIdTokenLifespan is the id token lifespan
// issued on an implicit grant.
ImplicitGrantIdTokenLifespan string `json:"implicit_grant_id_token_lifespan,omitempty"`
// +kubebuilder:validation:Pattern=[0-9]+(ns|us|ms|s|m|h)
//
// JwtBearerGrantAccessTokenLifespan is the access token lifespan
// issued on a jwt_bearer grant.
JwtBearerGrantAccessTokenLifespan string `json:"jwt_bearer_grant_access_token_lifespan,omitempty"`
// +kubebuilder:validation:Pattern=[0-9]+(ns|us|ms|s|m|h)
//
// RefreshTokenGrantAccessTokenLifespan is the access token lifespan
// issued on a refresh_token grant.
RefreshTokenGrantAccessTokenLifespan string `json:"refresh_token_grant_access_token_lifespan,omitempty"`
// +kubebuilder:validation:Pattern=[0-9]+(ns|us|ms|s|m|h)
//
// RefreshTokenGrantIdTokenLifespan is the id token lifespan
// issued on a refresh_token grant.
RefreshTokenGrantIdTokenLifespan string `json:"refresh_token_grant_id_token_lifespan,omitempty"`
// +kubebuilder:validation:Pattern=[0-9]+(ns|us|ms|s|m|h)
//
// RefreshTokenGrantRefreshTokenLifespan is the refresh token lifespan
// issued on a refresh_token grant.
RefreshTokenGrantRefreshTokenLifespan string `json:"refresh_token_grant_refresh_token_lifespan,omitempty"`
}
// OAuth2ClientSpec defines the desired state of OAuth2Client
type OAuth2ClientSpec struct {
@ -110,6 +173,10 @@ type OAuth2ClientSpec struct {
// Indication which authentication method shoud be used for the token endpoint
TokenEndpointAuthMethod TokenEndpointAuthMethod `json:"tokenEndpointAuthMethod,omitempty"`
// TokenLifespans is the configuration to use for managing different token lifespans
// depending on the used grant type.
TokenLifespans TokenLifespans `json:"tokenLifespans,omitempty"`
// +kubebuilder:validation:Type=object
// +nullable
// +optional

View File

@ -99,7 +99,17 @@ func TestCreateAPI(t *testing.T) {
"invalid hydra url": func() { created.Spec.HydraAdmin.URL = "invalid" },
"invalid hydra port high": func() { created.Spec.HydraAdmin.Port = 65536 },
"invalid hydra endpoint": func() { created.Spec.HydraAdmin.Endpoint = "invalid" },
"invalid hydra forwarded proto": func() { created.Spec.HydraAdmin.Endpoint = "invalid" },
"invalid hydra forwarded proto": func() { created.Spec.HydraAdmin.ForwardedProto = "invalid" },
"invalid lifespan authorization code access token": func() { created.Spec.TokenLifespans.AuthorizationCodeGrantAccessTokenLifespan = "invalid" },
"invalid lifespan authorization code id token": func() { created.Spec.TokenLifespans.AuthorizationCodeGrantIdTokenLifespan = "invalid" },
"invalid lifespan authorization code refresh token": func() { created.Spec.TokenLifespans.AuthorizationCodeGrantRefreshTokenLifespan = "invalid" },
"invalid lifespan client credentials access token": func() { created.Spec.TokenLifespans.ClientCredentialsGrantAccessTokenLifespan = "invalid" },
"invalid lifespan implicit access token": func() { created.Spec.TokenLifespans.ImplicitGrantAccessTokenLifespan = "invalid" },
"invalid lifespan implicit id token": func() { created.Spec.TokenLifespans.ImplicitGrantIdTokenLifespan = "invalid" },
"invalid lifespan jwt bearer access token": func() { created.Spec.TokenLifespans.JwtBearerGrantAccessTokenLifespan = "invalid" },
"invalid lifespan refresh token access token": func() { created.Spec.TokenLifespans.RefreshTokenGrantAccessTokenLifespan = "invalid" },
"invalid lifespan refresh token id token": func() { created.Spec.TokenLifespans.RefreshTokenGrantIdTokenLifespan = "invalid" },
"invalid lifespan refresh token refresh token": func() { created.Spec.TokenLifespans.RefreshTokenGrantRefreshTokenLifespan = "invalid" },
} {
t.Run(fmt.Sprintf("case=%s", desc), func(t *testing.T) {
resetTestClient()
@ -162,6 +172,7 @@ func resetTestClient() {
ResponseTypes: []ResponseType{"id_token", "code", "token"},
Scope: "read,write",
SecretName: "secret-name",
TokenLifespans: TokenLifespans{},
},
}
}

View File

@ -148,6 +148,7 @@ func (in *OAuth2ClientSpec) DeepCopyInto(out *OAuth2ClientSpec) {
copy(*out, *in)
}
out.HydraAdmin = in.HydraAdmin
out.TokenLifespans = in.TokenLifespans
in.Metadata.DeepCopyInto(&out.Metadata)
}
@ -196,3 +197,18 @@ func (in *ReconciliationError) DeepCopy() *ReconciliationError {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TokenLifespans) DeepCopyInto(out *TokenLifespans) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TokenLifespans.
func (in *TokenLifespans) DeepCopy() *TokenLifespans {
if in == nil {
return nil
}
out := new(TokenLifespans)
in.DeepCopyInto(out)
return out
}

View File

@ -233,6 +233,72 @@ spec:
Indication which authentication method shoud be used for the
token endpoint
type: string
tokenLifespans:
description: |-
TokenLifespans is the configuration to use for managing different token lifespans
depending on the used grant type.
properties:
authorization_code_grant_access_token_lifespan:
description: |-
AuthorizationCodeGrantAccessTokenLifespan is the access token lifespan
issued on an authorization_code grant.
pattern: "[0-9]+(ns|us|ms|s|m|h)"
type: string
authorization_code_grant_id_token_lifespan:
description: |-
AuthorizationCodeGrantIdTokenLifespan is the id token lifespan
issued on an authorization_code grant.
pattern: "[0-9]+(ns|us|ms|s|m|h)"
type: string
authorization_code_grant_refresh_token_lifespan:
description: |-
AuthorizationCodeGrantRefreshTokenLifespan is the refresh token lifespan
issued on an authorization_code grant.
pattern: "[0-9]+(ns|us|ms|s|m|h)"
type: string
client_credentials_grant_access_token_lifespan:
description: |-
AuthorizationCodeGrantRefreshTokenLifespan is the access token lifespan
issued on a client_credentials grant.
pattern: "[0-9]+(ns|us|ms|s|m|h)"
type: string
implicit_grant_access_token_lifespan:
description: |-
ImplicitGrantAccessTokenLifespan is the access token lifespan
issued on an implicit grant.
pattern: "[0-9]+(ns|us|ms|s|m|h)"
type: string
implicit_grant_id_token_lifespan:
description: |-
ImplicitGrantIdTokenLifespan is the id token lifespan
issued on an implicit grant.
pattern: "[0-9]+(ns|us|ms|s|m|h)"
type: string
jwt_bearer_grant_access_token_lifespan:
description: |-
JwtBearerGrantAccessTokenLifespan is the access token lifespan
issued on a jwt_bearer grant.
pattern: "[0-9]+(ns|us|ms|s|m|h)"
type: string
refresh_token_grant_access_token_lifespan:
description: |-
RefreshTokenGrantAccessTokenLifespan is the access token lifespan
issued on a refresh_token grant.
pattern: "[0-9]+(ns|us|ms|s|m|h)"
type: string
refresh_token_grant_id_token_lifespan:
description: |-
RefreshTokenGrantIdTokenLifespan is the id token lifespan
issued on a refresh_token grant.
pattern: "[0-9]+(ns|us|ms|s|m|h)"
type: string
refresh_token_grant_refresh_token_lifespan:
description: |-
RefreshTokenGrantRefreshTokenLifespan is the refresh token lifespan
issued on a refresh_token grant.
pattern: "[0-9]+(ns|us|ms|s|m|h)"
type: string
type: object
required:
- grantTypes
- scope

View File

@ -53,6 +53,7 @@ var testOAuthJSONPost = &hydra.OAuth2ClientJSON{
FrontChannelLogoutSessionRequired: false,
BackChannelLogoutURI: "https://localhost/backchannel-logout",
BackChannelLogoutSessionRequired: false,
AuthorizationCodeGrantAccessTokenLifespan: "6h",
}
var testOAuthJSONPut = &hydra.OAuth2ClientJSON{

View File

@ -33,6 +33,16 @@ type OAuth2ClientJSON struct {
FrontChannelLogoutURI string `json:"frontchannel_logout_uri"`
BackChannelLogoutSessionRequired bool `json:"backchannel_logout_session_required"`
BackChannelLogoutURI string `json:"backchannel_logout_uri"`
AuthorizationCodeGrantAccessTokenLifespan string `json:"authorization_code_grant_access_token_lifespan,omitempty"`
AuthorizationCodeGrantIdTokenLifespan string `json:"authorization_code_grant_id_token_lifespan,omitempty"`
AuthorizationCodeGrantRefreshTokenLifespan string `json:"authorization_code_grant_refresh_token_lifespan,omitempty"`
ClientCredentialsGrantAccessTokenLifespan string `json:"client_credentials_grant_access_token_lifespan,omitempty"`
ImplicitGrantAccessTokenLifespan string `json:"implicit_grant_access_token_lifespan,omitempty"`
ImplicitGrantIdTokenLifespan string `json:"implicit_grant_id_token_lifespan,omitempty"`
JwtBearerGrantAccessTokenLifespan string `json:"jwt_bearer_grant_access_token_lifespan,omitempty"`
RefreshTokenGrantAccessTokenLifespan string `json:"refresh_token_grant_access_token_lifespan,omitempty"`
RefreshTokenGrantIdTokenLifespan string `json:"refresh_token_grant_id_token_lifespan,omitempty"`
RefreshTokenGrantRefreshTokenLifespan string `json:"refresh_token_grant_refresh_token_lifespan,omitempty"`
}
// Oauth2ClientCredentials represents client ID and password fetched from a
@ -74,6 +84,16 @@ func FromOAuth2Client(c *hydrav1alpha1.OAuth2Client) (*OAuth2ClientJSON, error)
FrontChannelLogoutSessionRequired: c.Spec.BackChannelLogoutSessionRequired,
BackChannelLogoutSessionRequired: c.Spec.BackChannelLogoutSessionRequired,
BackChannelLogoutURI: c.Spec.BackChannelLogoutURI,
AuthorizationCodeGrantAccessTokenLifespan: c.Spec.TokenLifespans.AuthorizationCodeGrantAccessTokenLifespan,
AuthorizationCodeGrantIdTokenLifespan: c.Spec.TokenLifespans.AuthorizationCodeGrantIdTokenLifespan,
AuthorizationCodeGrantRefreshTokenLifespan: c.Spec.TokenLifespans.AuthorizationCodeGrantRefreshTokenLifespan,
ClientCredentialsGrantAccessTokenLifespan: c.Spec.TokenLifespans.ClientCredentialsGrantAccessTokenLifespan,
ImplicitGrantAccessTokenLifespan: c.Spec.TokenLifespans.ImplicitGrantAccessTokenLifespan,
ImplicitGrantIdTokenLifespan: c.Spec.TokenLifespans.ImplicitGrantIdTokenLifespan,
JwtBearerGrantAccessTokenLifespan: c.Spec.TokenLifespans.JwtBearerGrantAccessTokenLifespan,
RefreshTokenGrantAccessTokenLifespan: c.Spec.TokenLifespans.RefreshTokenGrantAccessTokenLifespan,
RefreshTokenGrantIdTokenLifespan: c.Spec.TokenLifespans.RefreshTokenGrantIdTokenLifespan,
RefreshTokenGrantRefreshTokenLifespan: c.Spec.TokenLifespans.RefreshTokenGrantRefreshTokenLifespan,
}, nil
}