Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement handling token lifespans #145

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions api/v1alpha1/oauth2client_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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
Expand Down
41 changes: 26 additions & 15 deletions api/v1alpha1/oauth2client_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,27 @@ func TestCreateAPI(t *testing.T) {
t.Run("by failing if the requested object doesn't meet CRD requirements", func(t *testing.T) {

for desc, modifyClient := range map[string]func(){
"invalid grant type": func() { created.Spec.GrantTypes = []GrantType{"invalid"} },
"invalid response type": func() { created.Spec.ResponseTypes = []ResponseType{"invalid", "code"} },
"invalid composite response type": func() { created.Spec.ResponseTypes = []ResponseType{"invalid code", "code id_token"} },
"invalid scope": func() { created.Spec.Scope = "" },
"missing secret name": func() { created.Spec.SecretName = "" },
"invalid redirect URI": func() { created.Spec.RedirectURIs = []RedirectURI{"invalid"} },
"invalid logout redirect URI": func() { created.Spec.PostLogoutRedirectURIs = []RedirectURI{"invalid"} },
"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 grant type": func() { created.Spec.GrantTypes = []GrantType{"invalid"} },
"invalid response type": func() { created.Spec.ResponseTypes = []ResponseType{"invalid", "code"} },
"invalid composite response type": func() { created.Spec.ResponseTypes = []ResponseType{"invalid code", "code id_token"} },
"invalid scope": func() { created.Spec.Scope = "" },
"missing secret name": func() { created.Spec.SecretName = "" },
"invalid redirect URI": func() { created.Spec.RedirectURIs = []RedirectURI{"invalid"} },
"invalid logout redirect URI": func() { created.Spec.PostLogoutRedirectURIs = []RedirectURI{"invalid"} },
"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.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()
Expand Down Expand Up @@ -158,10 +168,11 @@ func resetTestClient() {
Namespace: "default",
},
Spec: OAuth2ClientSpec{
GrantTypes: []GrantType{"implicit", "client_credentials", "authorization_code", "refresh_token"},
ResponseTypes: []ResponseType{"id_token", "code", "token"},
Scope: "read,write",
SecretName: "secret-name",
GrantTypes: []GrantType{"implicit", "client_credentials", "authorization_code", "refresh_token"},
ResponseTypes: []ResponseType{"id_token", "code", "token"},
Scope: "read,write",
SecretName: "secret-name",
TokenLifespans: TokenLifespans{},
},
}
}
16 changes: 16 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 66 additions & 0 deletions config/crd/bases/hydra.ory.sh_oauth2clients.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions hydra/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ var testOAuthJSONPost = &hydra.OAuth2ClientJSON{
FrontChannelLogoutSessionRequired: false,
BackChannelLogoutURI: "https://localhost/backchannel-logout",
BackChannelLogoutSessionRequired: false,
AuthorizationCodeGrantAccessTokenLifespan: "6h",
}

var testOAuthJSONPut = &hydra.OAuth2ClientJSON{
Expand Down
58 changes: 39 additions & 19 deletions hydra/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,35 @@ import (

// OAuth2ClientJSON represents an OAuth2 client digestible by ORY Hydra
type OAuth2ClientJSON struct {
ClientName string `json:"client_name,omitempty"`
ClientID *string `json:"client_id,omitempty"`
Secret *string `json:"client_secret,omitempty"`
GrantTypes []string `json:"grant_types"`
RedirectURIs []string `json:"redirect_uris,omitempty"`
PostLogoutRedirectURIs []string `json:"post_logout_redirect_uris,omitempty"`
AllowedCorsOrigins []string `json:"allowed_cors_origins,omitempty"`
ResponseTypes []string `json:"response_types,omitempty"`
Audience []string `json:"audience,omitempty"`
Scope string `json:"scope"`
SkipConsent bool `json:"skip_consent,omitempty"`
Owner string `json:"owner"`
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
Metadata json.RawMessage `json:"metadata,omitempty"`
JwksUri string `json:"jwks_uri,omitempty"`
FrontChannelLogoutSessionRequired bool `json:"frontchannel_logout_session_required"`
FrontChannelLogoutURI string `json:"frontchannel_logout_uri"`
BackChannelLogoutSessionRequired bool `json:"backchannel_logout_session_required"`
BackChannelLogoutURI string `json:"backchannel_logout_uri"`
ClientName string `json:"client_name,omitempty"`
ClientID *string `json:"client_id,omitempty"`
Secret *string `json:"client_secret,omitempty"`
GrantTypes []string `json:"grant_types"`
RedirectURIs []string `json:"redirect_uris,omitempty"`
PostLogoutRedirectURIs []string `json:"post_logout_redirect_uris,omitempty"`
AllowedCorsOrigins []string `json:"allowed_cors_origins,omitempty"`
ResponseTypes []string `json:"response_types,omitempty"`
Audience []string `json:"audience,omitempty"`
Scope string `json:"scope"`
SkipConsent bool `json:"skip_consent,omitempty"`
Owner string `json:"owner"`
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
Metadata json.RawMessage `json:"metadata,omitempty"`
JwksUri string `json:"jwks_uri,omitempty"`
FrontChannelLogoutSessionRequired bool `json:"frontchannel_logout_session_required"`
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
Expand Down Expand Up @@ -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
}

Expand Down
Loading