Skip to content

Commit

Permalink
feat: add prompt=registration (#3636)
Browse files Browse the repository at this point in the history
Ory Hydra now supports a `registration` value for the `prompt` parameter of
the authorization request. When specifying `prompt=registration`, Ory Hydra
will redirect the user to the URL found under `urls.registration`
(instead of `urls.login`).
  • Loading branch information
hperl committed Sep 29, 2023
1 parent ada59a5 commit 19857d2
Show file tree
Hide file tree
Showing 6 changed files with 414 additions and 24 deletions.
11 changes: 9 additions & 2 deletions consent/strategy_default.go
Expand Up @@ -204,7 +204,7 @@ func (s *DefaultStrategy) forwardAuthenticationRequest(ctx context.Context, w ht
skip = true
}

// Let'id validate that prompt is actually not "none" if we can't skip authentication
// Let's validate that prompt is actually not "none" if we can't skip authentication
prompt := stringsx.Splitx(ar.GetRequestForm().Get("prompt"), " ")
if stringslice.Has(prompt, "none") && !skip {
return errorsx.WithStack(fosite.ErrLoginRequired.WithHint(`Prompt 'none' was requested, but no existing login session was found.`))
Expand Down Expand Up @@ -293,7 +293,14 @@ func (s *DefaultStrategy) forwardAuthenticationRequest(ctx context.Context, w ht
return err
}

http.Redirect(w, r, urlx.SetQuery(s.c.LoginURL(ctx), url.Values{"login_challenge": {encodedFlow}}).String(), http.StatusFound)
var baseURL *url.URL
if stringslice.Has(prompt, "registration") {
baseURL = s.c.RegistrationURL(ctx)
} else {
baseURL = s.c.LoginURL(ctx)
}

http.Redirect(w, r, urlx.SetQuery(baseURL, url.Values{"login_challenge": {encodedFlow}}).String(), http.StatusFound)

// generate the verifier
return errorsx.WithStack(ErrAbortOAuth2Request)
Expand Down
5 changes: 5 additions & 0 deletions driver/config/provider.go
Expand Up @@ -78,6 +78,7 @@ const (
KeyGetSystemSecret = "secrets.system"
KeyLogoutRedirectURL = "urls.post_logout_redirect"
KeyLoginURL = "urls.login"
KeyRegistrationURL = "urls.registration"
KeyLogoutURL = "urls.logout"
KeyConsentURL = "urls.consent"
KeyErrorURL = "urls.error"
Expand Down Expand Up @@ -375,6 +376,10 @@ func (p *DefaultProvider) LoginURL(ctx context.Context) *url.URL {
return urlRoot(p.getProvider(ctx).URIF(KeyLoginURL, p.publicFallbackURL(ctx, "oauth2/fallbacks/login")))
}

func (p *DefaultProvider) RegistrationURL(ctx context.Context) *url.URL {
return urlRoot(p.getProvider(ctx).URIF(KeyRegistrationURL, p.LoginURL(ctx)))
}

func (p *DefaultProvider) LogoutURL(ctx context.Context) *url.URL {
return urlRoot(p.getProvider(ctx).RequestURIF(KeyLogoutURL, p.publicFallbackURL(ctx, "oauth2/fallbacks/logout")))
}
Expand Down
44 changes: 22 additions & 22 deletions fositex/config.go
Expand Up @@ -87,101 +87,101 @@ func (c *Config) LoadDefaultHandlers(strategy interface{}) {
}
}

func (c *Config) GetJWKSFetcherStrategy(ctx context.Context) fosite.JWKSFetcherStrategy {
func (c *Config) GetJWKSFetcherStrategy(context.Context) fosite.JWKSFetcherStrategy {
return c.deps.GetJWKSFetcherStrategy()
}

func (c *Config) GetHTTPClient(ctx context.Context) *retryablehttp.Client {
return c.deps.HTTPClient(ctx)
}

func (c *Config) GetAuthorizeEndpointHandlers(ctx context.Context) fosite.AuthorizeEndpointHandlers {
func (c *Config) GetAuthorizeEndpointHandlers(context.Context) fosite.AuthorizeEndpointHandlers {
return c.authorizeEndpointHandlers
}

func (c *Config) GetTokenEndpointHandlers(ctx context.Context) fosite.TokenEndpointHandlers {
func (c *Config) GetTokenEndpointHandlers(context.Context) fosite.TokenEndpointHandlers {
return c.tokenEndpointHandlers
}

func (c *Config) GetTokenIntrospectionHandlers(ctx context.Context) (r fosite.TokenIntrospectionHandlers) {
func (c *Config) GetTokenIntrospectionHandlers(context.Context) (r fosite.TokenIntrospectionHandlers) {
return c.tokenIntrospectionHandlers
}

func (c *Config) GetRevocationHandlers(ctx context.Context) fosite.RevocationHandlers {
func (c *Config) GetRevocationHandlers(context.Context) fosite.RevocationHandlers {
return c.revocationHandlers
}

func (c *Config) GetGrantTypeJWTBearerCanSkipClientAuth(ctx context.Context) bool {
func (c *Config) GetGrantTypeJWTBearerCanSkipClientAuth(context.Context) bool {
return false
}

func (c *Config) GetAudienceStrategy(ctx context.Context) fosite.AudienceMatchingStrategy {
func (c *Config) GetAudienceStrategy(context.Context) fosite.AudienceMatchingStrategy {
return fosite.DefaultAudienceMatchingStrategy
}

func (c *Config) GetOmitRedirectScopeParam(ctx context.Context) bool {
func (c *Config) GetOmitRedirectScopeParam(context.Context) bool {
return false
}

func (c *Config) GetSanitationWhiteList(ctx context.Context) []string {
func (c *Config) GetSanitationWhiteList(context.Context) []string {
return []string{"code", "redirect_uri"}
}

func (c *Config) GetEnablePKCEPlainChallengeMethod(ctx context.Context) bool {
func (c *Config) GetEnablePKCEPlainChallengeMethod(context.Context) bool {
return false
}

func (c *Config) GetDisableRefreshTokenValidation(ctx context.Context) bool {
func (c *Config) GetDisableRefreshTokenValidation(context.Context) bool {
return false
}

func (c *Config) GetRefreshTokenScopes(ctx context.Context) []string {
func (c *Config) GetRefreshTokenScopes(context.Context) []string {
return []string{"offline", "offline_access"}
}

func (c *Config) GetMinParameterEntropy(_ context.Context) int {
return fosite.MinParameterEntropy
}

func (c *Config) GetClientAuthenticationStrategy(ctx context.Context) fosite.ClientAuthenticationStrategy {
func (c *Config) GetClientAuthenticationStrategy(context.Context) fosite.ClientAuthenticationStrategy {
// Fosite falls back to the default fosite.Fosite.DefaultClientAuthenticationStrategy when this is nil.
return nil
}

func (c *Config) GetResponseModeHandlerExtension(ctx context.Context) fosite.ResponseModeHandler {
func (c *Config) GetResponseModeHandlerExtension(context.Context) fosite.ResponseModeHandler {
return defaultResponseModeHandler
}

func (c *Config) GetSendDebugMessagesToClients(ctx context.Context) bool {
return c.deps.Config().GetSendDebugMessagesToClients(ctx)
}

func (c *Config) GetMessageCatalog(ctx context.Context) i18n.MessageCatalog {
func (c *Config) GetMessageCatalog(context.Context) i18n.MessageCatalog {
// Fosite falls back to the default messages when this is nil.
return nil
}

func (c *Config) GetSecretsHasher(ctx context.Context) fosite.Hasher {
func (c *Config) GetSecretsHasher(context.Context) fosite.Hasher {
return c.deps.ClientHasher()
}

func (c *Config) GetTokenEntropy(ctx context.Context) int {
func (c *Config) GetTokenEntropy(context.Context) int {
return 32
}

func (c *Config) GetHMACHasher(ctx context.Context) func() hash.Hash {
func (c *Config) GetHMACHasher(context.Context) func() hash.Hash {
return sha512.New512_256
}

func (c *Config) GetIDTokenIssuer(ctx context.Context) string {
return c.deps.Config().IssuerURL(ctx).String()
}

func (c *Config) GetAllowedPrompts(ctx context.Context) []string {
return []string{"login", "none", "consent"}
func (c *Config) GetAllowedPrompts(context.Context) []string {
return []string{"login", "none", "consent", "registration"}
}

func (c *Config) GetRedirectSecureChecker(ctx context.Context) func(context.Context, *url.URL) bool {
func (c *Config) GetRedirectSecureChecker(context.Context) func(context.Context, *url.URL) bool {
return x.IsRedirectURISecure(c.deps.Config())
}

Expand All @@ -193,7 +193,7 @@ func (c *Config) GetJWTScopeField(ctx context.Context) jwt.JWTScopeFieldEnum {
return c.deps.Config().GetJWTScopeField(ctx)
}

func (c *Config) GetFormPostHTMLTemplate(ctx context.Context) *template.Template {
func (c *Config) GetFormPostHTMLTemplate(context.Context) *template.Template {
return fosite.DefaultFormPostTemplate
}

Expand Down

0 comments on commit 19857d2

Please sign in to comment.