Skip to content

Commit

Permalink
refactor: rfc8628
Browse files Browse the repository at this point in the history
  • Loading branch information
james-d-elliott committed Mar 10, 2024
1 parent 5f021e9 commit 53140ef
Show file tree
Hide file tree
Showing 22 changed files with 187 additions and 183 deletions.
2 changes: 1 addition & 1 deletion compose/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func Compose(config *oauth2.Config, storage any, strategy any, factories ...Fact
config.PushedAuthorizeEndpointHandlers.Append(ph)
}
if dh, ok := res.(oauth2.RFC8628DeviceAuthorizeEndpointHandler); ok {
config.DeviceAuthorizeEndpointHandlers.Append(dh)
config.RFC8628DeviceAuthorizeEndpointHandlers.Append(dh)
}
if uh, ok := res.(oauth2.RFC8628UserAuthorizeEndpointHandler); ok {
config.RFC8628UserAuthorizeEndpointHandlers.Append(uh)
Expand Down
2 changes: 1 addition & 1 deletion compose/compose_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type HMACSHAStrategyConfigurator interface {
oauth2.GlobalSecretProvider
oauth2.RotatedGlobalSecretsProvider
oauth2.HMACHashingProvider
oauth2.DeviceAuthorizeConfigProvider
oauth2.RFC9628DeviceAuthorizeConfigProvider
}

func NewOAuth2HMACStrategy(config HMACSHAStrategyConfigurator) *hoauth2.HMACSHAStrategy {
Expand Down
31 changes: 17 additions & 14 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,32 +313,35 @@ type PushedAuthorizeRequestHandlersProvider interface {
GetPushedAuthorizeEndpointHandlers(ctx context.Context) PushedAuthorizeEndpointHandlers
}

// DeviceAuthorizeConfigProvider returns the provider for configuring the device authorization response
// (see https://www.rfc-editor.org/rfc/rfc8628#section-3.2)
type DeviceAuthorizeConfigProvider interface {
// GetDeviceAndUserCodeLifespan returns the device and user code lifespan.
GetDeviceAndUserCodeLifespan(ctx context.Context) time.Duration
GetRFC8628UserVerificationURL(ctx context.Context) string
GetDeviceAuthTokenPollingInterval(ctx context.Context) time.Duration
// RFC9628DeviceAuthorizeConfigProvider returns the provider for configuring the device authorization response.
//
// See: https://www.rfc-editor.org/rfc/rfc8628#section-3.2
type RFC9628DeviceAuthorizeConfigProvider interface {
// GetRFC8628CodeLifespan returns the device and user code lifespan.
GetRFC8628CodeLifespan(ctx context.Context) (lifespan time.Duration)

GetRFC8628UserVerificationURL(ctx context.Context) (url string)

GetRFC8628TokenPollingInterval(ctx context.Context) (interval time.Duration)
}

// DeviceAuthorizeEndpointHandlersProvider returns the provider for setting up the Device authorization handlers.
type DeviceAuthorizeEndpointHandlersProvider interface {
// GetDeviceAuthorizeEndpointHandlers returns the handlers.
GetDeviceAuthorizeEndpointHandlers(ctx context.Context) DeviceAuthorizeEndpointHandlers
// RFC8628DeviceAuthorizeEndpointHandlersProvider returns the provider for setting up the Device authorization handlers.
type RFC8628DeviceAuthorizeEndpointHandlersProvider interface {
// GetRFC8628DeviceAuthorizeEndpointHandlers returns the handlers.
GetRFC8628DeviceAuthorizeEndpointHandlers(ctx context.Context) RFC8628DeviceAuthorizeEndpointHandlers
}

// RFC8628UserAuthorizeEndpointHandlersProvider returns the provider for setting up the Device grant user interaction handlers.
type RFC8628UserAuthorizeEndpointHandlersProvider interface {

// GetRFC8628UserAuthorizeEndpointHandlers returns the handlers.
GetRFC8628UserAuthorizeEndpointHandlers(ctx context.Context) RFC8628UserAuthorizeEndpointHandlers
}

// RFC8693ConfigProvider is the configuration provider for RFC8693 Token Exchange.
type RFC8693ConfigProvider interface {
GetTokenTypes(ctx context.Context) map[string]RFC8693TokenType
GetRFC8693TokenTypes(ctx context.Context) map[string]RFC8693TokenType

GetDefaultRequestedTokenType(ctx context.Context) string
GetDefaultRFC8693RequestedTokenType(ctx context.Context) string
}

// UseLegacyErrorFormatProvider returns the provider for configuring whether to use the legacy error format.
Expand Down
43 changes: 22 additions & 21 deletions config_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type Config struct {
IDTokenLifespan time.Duration

// Sets how long a device user/device code pair is valid for
DeviceAndUserCodeLifespan time.Duration
RFC8628CodeLifespan time.Duration

// IDTokenIssuer sets the default issuer of the ID Token.
IDTokenIssuer string
Expand Down Expand Up @@ -89,9 +89,6 @@ type Config struct {
// this value MUST be set.
TokenURL string

// RFC8628UserVerificationURL is the URL of the device verification endpoint, this is is included with the device code request responses
RFC8628UserVerificationURL string

// JWKSFetcherStrategy is responsible for fetching JSON Web Keys from remote URLs. This is required when the private_key_jwt
// client authentication method is used. Defaults to oauth2.DefaultJWKSFetcherStrategy.
JWKSFetcherStrategy JWKSFetcherStrategy
Expand All @@ -100,8 +97,11 @@ type Config struct {
// Defaults to 32.
TokenEntropy int

// DeviceAuthTokenPollingInterval sets the interval that clients should check for device code grants
DeviceAuthTokenPollingInterval time.Duration
// RFC8628UserVerificationURL is the URL of the device verification endpoint, this is is included with the device code request responses
RFC8628UserVerificationURL string

// RFC8628TokenPollingInterval sets the interval that clients should check for device code grants
RFC8628TokenPollingInterval time.Duration

// RedirectSecureChecker is a function that returns true if the provided URL can be securely used as a redirect URL.
RedirectSecureChecker func(context.Context, *url.URL) bool
Expand Down Expand Up @@ -184,8 +184,8 @@ type Config struct {
// PushedAuthorizeEndpointHandlers is a list of handlers that are called before the PAR endpoint is served.
PushedAuthorizeEndpointHandlers PushedAuthorizeEndpointHandlers

// DeviceAuthorizeEndpointHandlers is a list of handlers that are called before the device authorization endpoint is served.
DeviceAuthorizeEndpointHandlers DeviceAuthorizeEndpointHandlers
// RFC8628DeviceAuthorizeEndpointHandlers is a list of handlers that are called before the device authorization endpoint is served.
RFC8628DeviceAuthorizeEndpointHandlers RFC8628DeviceAuthorizeEndpointHandlers

// RFC8628UserAuthorizeEndpointHandlers is a list of handlers that are called before the device grant user interaction endpoint is served.
RFC8628UserAuthorizeEndpointHandlers RFC8628UserAuthorizeEndpointHandlers
Expand Down Expand Up @@ -246,8 +246,8 @@ func (c *Config) GetRevocationHandlers(ctx context.Context) RevocationHandlers {
return c.RevocationHandlers
}

func (c *Config) GetDeviceAuthorizeEndpointHandlers(_ context.Context) DeviceAuthorizeEndpointHandlers {
return c.DeviceAuthorizeEndpointHandlers
func (c *Config) GetRFC8628DeviceAuthorizeEndpointHandlers(_ context.Context) RFC8628DeviceAuthorizeEndpointHandlers {
return c.RFC8628DeviceAuthorizeEndpointHandlers
}

func (c *Config) GetRFC8628UserAuthorizeEndpointHandlers(_ context.Context) RFC8628UserAuthorizeEndpointHandlers {
Expand Down Expand Up @@ -406,12 +406,13 @@ func (c *Config) GetIDTokenLifespan(_ context.Context) time.Duration {
return c.IDTokenLifespan
}

// GetDeviceAndUserCodeLifespan returns the device and user code lifespan.
func (c *Config) GetDeviceAndUserCodeLifespan(_ context.Context) time.Duration {
if c.DeviceAndUserCodeLifespan == 0 {
// GetRFC8628CodeLifespan returns the device and user code lifespan.
func (c *Config) GetRFC8628CodeLifespan(_ context.Context) time.Duration {
if c.RFC8628CodeLifespan == 0 {
return time.Minute * 10
}
return c.DeviceAndUserCodeLifespan

return c.RFC8628CodeLifespan
}

// GetAccessTokenLifespan returns how long an access token should be valid. Defaults to one hour.
Expand Down Expand Up @@ -561,23 +562,23 @@ func (c *Config) EnforcePushedAuthorize(ctx context.Context) bool {
return c.IsPushedAuthorizeEnforced
}

func (c *Config) GetTokenTypes(ctx context.Context) map[string]RFC8693TokenType {
func (c *Config) GetRFC8693TokenTypes(ctx context.Context) map[string]RFC8693TokenType {
return c.RFC8693TokenTypes
}

func (c *Config) GetDefaultRequestedTokenType(ctx context.Context) string {
func (c *Config) GetDefaultRFC8693RequestedTokenType(ctx context.Context) string {
return c.DefaultRequestedTokenType
}

func (c *Config) GetRFC8628UserVerificationURL(_ context.Context) string {
return c.RFC8628UserVerificationURL
}

func (c *Config) GetDeviceAuthTokenPollingInterval(_ context.Context) time.Duration {
if c.DeviceAuthTokenPollingInterval == 0 {
func (c *Config) GetRFC8628TokenPollingInterval(_ context.Context) time.Duration {
if c.RFC8628TokenPollingInterval == 0 {
return time.Second * 10
}
return c.DeviceAuthTokenPollingInterval
return c.RFC8628TokenPollingInterval
}

var (
Expand Down Expand Up @@ -625,7 +626,7 @@ var (
_ PushedAuthorizeRequestHandlersProvider = (*Config)(nil)
_ PushedAuthorizeRequestConfigProvider = (*Config)(nil)
_ RFC8693ConfigProvider = (*Config)(nil)
_ DeviceAuthorizeConfigProvider = (*Config)(nil)
_ DeviceAuthorizeEndpointHandlersProvider = (*Config)(nil)
_ RFC9628DeviceAuthorizeConfigProvider = (*Config)(nil)
_ RFC8628DeviceAuthorizeEndpointHandlersProvider = (*Config)(nil)
_ RFC8628UserAuthorizeEndpointHandlersProvider = (*Config)(nil)
)
10 changes: 5 additions & 5 deletions fosite.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ func (a *PushedAuthorizeEndpointHandlers) Append(h PushedAuthorizeEndpointHandle
*a = append(*a, h)
}

// DeviceAuthorizeEndpointHandlers is a list of RFC8628DeviceAuthorizeEndpointHandler
type DeviceAuthorizeEndpointHandlers []RFC8628DeviceAuthorizeEndpointHandler
// RFC8628DeviceAuthorizeEndpointHandlers is a list of RFC8628DeviceAuthorizeEndpointHandler
type RFC8628DeviceAuthorizeEndpointHandlers []RFC8628DeviceAuthorizeEndpointHandler

// Append adds an RFC8628DeviceAuthorizeEndpointHandler to this list. Ignores duplicates based on reflect.TypeOf.
func (a *DeviceAuthorizeEndpointHandlers) Append(h RFC8628DeviceAuthorizeEndpointHandler) {
func (a *RFC8628DeviceAuthorizeEndpointHandlers) Append(h RFC8628DeviceAuthorizeEndpointHandler) {
for _, this := range *a {
if reflect.TypeOf(this) == reflect.TypeOf(h) {
return
Expand Down Expand Up @@ -162,9 +162,9 @@ type Configurator interface {
PushedAuthorizeRequestHandlersProvider
PushedAuthorizeRequestConfigProvider
RFC8693ConfigProvider
DeviceAuthorizeEndpointHandlersProvider
RFC8628DeviceAuthorizeEndpointHandlersProvider
RFC8628UserAuthorizeEndpointHandlersProvider
DeviceAuthorizeConfigProvider
RFC9628DeviceAuthorizeConfigProvider
UseLegacyErrorFormatProvider
}

Expand Down
12 changes: 6 additions & 6 deletions handler/openid/flow_device_authorization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ func TestOpenIDConnectDeviceAuthorizeHandler_PopulateRFC8628UserAuthorizeEndpoin
defer ctrl.Finish()

config := &oauth2.Config{
AccessTokenLifespan: time.Minute * 24,
AuthorizeCodeLifespan: time.Minute * 24,
DeviceAndUserCodeLifespan: time.Minute * 24,
AccessTokenLifespan: time.Minute * 24,
AuthorizeCodeLifespan: time.Minute * 24,
RFC8628CodeLifespan: time.Minute * 24,
}
j := &DefaultStrategy{
Signer: &jwt.DefaultSigner{
Expand Down Expand Up @@ -124,9 +124,9 @@ func TestOpenIDConnectDeviceAuthorizeHandler_PopulateTokenEndpointResponse(t *te
defer ctrl.Finish()

config := &oauth2.Config{
AccessTokenLifespan: time.Minute * 24,
AuthorizeCodeLifespan: time.Minute * 24,
DeviceAndUserCodeLifespan: time.Minute * 24,
AccessTokenLifespan: time.Minute * 24,
AuthorizeCodeLifespan: time.Minute * 24,
RFC8628CodeLifespan: time.Minute * 24,
}
j := &DefaultStrategy{
Signer: &jwt.DefaultSigner{
Expand Down
6 changes: 3 additions & 3 deletions handler/rfc8628/device_authorize_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type DeviceAuthorizeHandler struct {
Storage RFC8628Storage
Strategy RFC8628CodeStrategy
Config interface {
oauth2.DeviceAuthorizeConfigProvider
oauth2.RFC9628DeviceAuthorizeConfigProvider
}
}

Expand All @@ -38,7 +38,7 @@ func (d *DeviceAuthorizeHandler) HandleRFC8628DeviceAuthorizeEndpointRequest(ctx
dar.SetDeviceCodeSignature(deviceCodeSignature)
dar.SetUserCodeSignature(userCodeSignature)

expireAt := time.Now().UTC().Add(d.Config.GetDeviceAndUserCodeLifespan(ctx)).Round(time.Second)
expireAt := time.Now().UTC().Add(d.Config.GetRFC8628CodeLifespan(ctx)).Round(time.Second)
session.SetExpiresAt(oauth2.DeviceCode, expireAt)
session.SetExpiresAt(oauth2.UserCode, expireAt)

Expand Down Expand Up @@ -67,7 +67,7 @@ func (d *DeviceAuthorizeHandler) HandleRFC8628DeviceAuthorizeEndpointRequest(ctx
resp.SetVerificationURI(raw)
resp.SetVerificationURIComplete(uri.String())
resp.SetExpiresIn(int64(time.Until(expireAt).Seconds()))
resp.SetInterval(int(d.Config.GetDeviceAuthTokenPollingInterval(ctx).Seconds()))
resp.SetInterval(int(d.Config.GetRFC8628TokenPollingInterval(ctx).Seconds()))

return nil
}
Expand Down
22 changes: 11 additions & 11 deletions handler/rfc8628/device_authorize_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import (
func Test_HandleDeviceEndpointRequest(t *testing.T) {
strategy := NewRFC8628HMACSHAStrategy(&hmac.HMACStrategy{Config: &oauth2.Config{GlobalSecret: []byte("foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar")}},
&oauth2.Config{
AccessTokenLifespan: time.Minute * 24,
AuthorizeCodeLifespan: time.Minute * 24,
DeviceAndUserCodeLifespan: time.Minute * 24,
AccessTokenLifespan: time.Minute * 24,
AuthorizeCodeLifespan: time.Minute * 24,
RFC8628CodeLifespan: time.Minute * 24,
}, "authelia_%s_")

ctrl := gomock.NewController(t)
Expand All @@ -32,14 +32,14 @@ func Test_HandleDeviceEndpointRequest(t *testing.T) {
Storage: store,
Strategy: strategy,
Config: &oauth2.Config{
DeviceAndUserCodeLifespan: time.Minute * 10,
DeviceAuthTokenPollingInterval: time.Second * 10,
RFC8628UserVerificationURL: "https://www.test.com",
AccessTokenLifespan: time.Hour,
RefreshTokenLifespan: time.Hour,
ScopeStrategy: oauth2.HierarchicScopeStrategy,
AudienceMatchingStrategy: oauth2.DefaultAudienceMatchingStrategy,
RefreshTokenScopes: []string{consts.ScopeOffline},
RFC8628CodeLifespan: time.Minute * 10,
RFC8628TokenPollingInterval: time.Second * 10,
RFC8628UserVerificationURL: "https://www.test.com",
AccessTokenLifespan: time.Hour,
RefreshTokenLifespan: time.Hour,
ScopeStrategy: oauth2.HierarchicScopeStrategy,
AudienceMatchingStrategy: oauth2.DefaultAudienceMatchingStrategy,
RefreshTokenScopes: []string{consts.ScopeOffline},
},
}

Expand Down
12 changes: 6 additions & 6 deletions handler/rfc8628/strategy_hmacsha.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
enigma "authelia.com/provider/oauth2/token/hmac"
)

func NewRFC8628HMACSHAStrategy(enigma *enigma.HMACStrategy, config oauth2.DeviceAuthorizeConfigProvider, prefix string) *RFC8628HMACSHAStrategy {
func NewRFC8628HMACSHAStrategy(enigma *enigma.HMACStrategy, config oauth2.RFC9628DeviceAuthorizeConfigProvider, prefix string) *RFC8628HMACSHAStrategy {
return &RFC8628HMACSHAStrategy{
Enigma: enigma,
Config: config,
Expand All @@ -23,7 +23,7 @@ func NewRFC8628HMACSHAStrategy(enigma *enigma.HMACStrategy, config oauth2.Device
type RFC8628HMACSHAStrategy struct {
Enigma *enigma.HMACStrategy
Config interface {
oauth2.DeviceAuthorizeConfigProvider
oauth2.RFC9628DeviceAuthorizeConfigProvider
}

prefix string
Expand Down Expand Up @@ -52,8 +52,8 @@ func (h *RFC8628HMACSHAStrategy) UserCodeSignature(ctx context.Context, token st
func (h *RFC8628HMACSHAStrategy) ValidateUserCode(ctx context.Context, r oauth2.Requester, code string) (err error) {
var exp = r.GetSession().GetExpiresAt(oauth2.UserCode)

if exp.IsZero() && r.GetRequestedAt().Add(h.Config.GetDeviceAndUserCodeLifespan(ctx)).Before(time.Now().UTC()) {
return errorsx.WithStack(oauth2.ErrDeviceExpiredToken.WithHintf("User code expired at '%s'.", r.GetRequestedAt().Add(h.Config.GetDeviceAndUserCodeLifespan(ctx))))
if exp.IsZero() && r.GetRequestedAt().Add(h.Config.GetRFC8628CodeLifespan(ctx)).Before(time.Now().UTC()) {
return errorsx.WithStack(oauth2.ErrDeviceExpiredToken.WithHintf("User code expired at '%s'.", r.GetRequestedAt().Add(h.Config.GetRFC8628CodeLifespan(ctx))))
}

if !exp.IsZero() && exp.Before(time.Now().UTC()) {
Expand All @@ -79,8 +79,8 @@ func (h *RFC8628HMACSHAStrategy) DeviceCodeSignature(ctx context.Context, token
func (h *RFC8628HMACSHAStrategy) ValidateDeviceCode(ctx context.Context, r oauth2.Requester, code string) (err error) {
var exp = r.GetSession().GetExpiresAt(oauth2.DeviceCode)

if exp.IsZero() && r.GetRequestedAt().Add(h.Config.GetDeviceAndUserCodeLifespan(ctx)).Before(time.Now().UTC()) {
return errorsx.WithStack(oauth2.ErrDeviceExpiredToken.WithHintf("Device code expired at '%s'.", r.GetRequestedAt().Add(h.Config.GetDeviceAndUserCodeLifespan(ctx))))
if exp.IsZero() && r.GetRequestedAt().Add(h.Config.GetRFC8628CodeLifespan(ctx)).Before(time.Now().UTC()) {
return errorsx.WithStack(oauth2.ErrDeviceExpiredToken.WithHintf("Device code expired at '%s'.", r.GetRequestedAt().Add(h.Config.GetRFC8628CodeLifespan(ctx))))
}

if !exp.IsZero() && exp.Before(time.Now().UTC()) {
Expand Down
12 changes: 6 additions & 6 deletions handler/rfc8628/strategy_hmacsha_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import (
var hmacshaStrategy = RFC8628HMACSHAStrategy{
Enigma: &hmac.HMACStrategy{Config: &oauth2.Config{GlobalSecret: []byte("foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar")}},
Config: &oauth2.Config{
AccessTokenLifespan: time.Minute * 24,
AuthorizeCodeLifespan: time.Minute * 24,
DeviceAndUserCodeLifespan: time.Minute * 24,
AccessTokenLifespan: time.Minute * 24,
AuthorizeCodeLifespan: time.Minute * 24,
RFC8628CodeLifespan: time.Minute * 24,
},
}

Expand Down Expand Up @@ -121,9 +121,9 @@ func TestHMACDeviceCode(t *testing.T) {
t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) {
strategy := NewRFC8628HMACSHAStrategy(&hmac.HMACStrategy{Config: &oauth2.Config{GlobalSecret: []byte("foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar")}},
&oauth2.Config{
AccessTokenLifespan: time.Minute * 24,
AuthorizeCodeLifespan: time.Minute * 24,
DeviceAndUserCodeLifespan: time.Minute * 24,
AccessTokenLifespan: time.Minute * 24,
AuthorizeCodeLifespan: time.Minute * 24,
RFC8628CodeLifespan: time.Minute * 24,
}, "authelia_%s_")

token, signature, err := strategy.GenerateDeviceCode(context.TODO())
Expand Down
4 changes: 2 additions & 2 deletions handler/rfc8628/token_endpoint_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type DeviceCodeTokenHandler struct {
Storage RFC8628Storage
Strategy RFC8628CodeStrategy
Config interface {
oauth2.DeviceAuthorizeConfigProvider
oauth2.RFC9628DeviceAuthorizeConfigProvider
}
}

Expand Down Expand Up @@ -60,7 +60,7 @@ func (c *DeviceCodeTokenHandler) GetCodeAndSession(ctx context.Context, requeste
requestedAt = time.Now()
}

pollInterval := c.Config.GetDeviceAuthTokenPollingInterval(ctx)
pollInterval := c.Config.GetRFC8628TokenPollingInterval(ctx)
if lastReqTime.Add(pollInterval).After(requestedAt) {
_ = c.UpdateLastChecked(ctx, requester, deviceAuthReq)
return code, signature, deviceAuthReq, errorsx.WithStack(
Expand Down
Loading

0 comments on commit 53140ef

Please sign in to comment.