Skip to content

Commit

Permalink
feat: add ability to override oidc discovery urls
Browse files Browse the repository at this point in the history
Added config options `webfinger.oidc_discovery.token_url`, `webfinger.oidc_discovery.auth_url`, `webfinger.oidc_discovery.jwks_url`.
  • Loading branch information
aeneasr committed Nov 17, 2020
1 parent 88ddd90 commit bb8b982
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 23 deletions.
25 changes: 25 additions & 0 deletions .schema/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,32 @@
"additionalProperties": false,
"description": "Configures OpenID Connect Discovery (/.well-known/openid-configuration).",
"properties": {
"jwks_url": {
"type": "string",
"description": "Overwrites the JWKS URL",
"format": "uri",
"examples": [
"https://my-service.com/.well-known/jwks.json"
]
},
"token_url": {
"type": "string",
"description": "Overwrites the OAuth2 Token URL",
"format": "uri",
"examples": [
"https://my-service.com/oauth2/token"
]
},
"auth_url": {
"type": "string",
"description": "Overwrites the OAuth2 Auth URL",
"format": "uri",
"examples": [
"https://my-service.com/oauth2/auth"
]
},
"client_registration_url": {
"description": "Sets the OpenID Connect Dynamic Client Registration Endpoint",
"type": "string",
"format": "uri",
"examples": [
Expand Down
2 changes: 1 addition & 1 deletion consent/strategy_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func (s *DefaultStrategy) forwardAuthenticationRequest(w http.ResponseWriter, r
csrf := strings.Replace(uuid.New(), "-", "", -1)

// Generate the request URL
iu := urlx.AppendPaths(s.c.IssuerURL(), s.c.OAuth2AuthURL())
iu := s.c.OAuth2AuthURL()
iu.RawQuery = r.URL.RawQuery

var idTokenHintClaims jwtgo.MapClaims
Expand Down
4 changes: 3 additions & 1 deletion driver/configuration/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ type Provider interface {
ErrorURL() *url.URL
PublicURL() *url.URL
IssuerURL() *url.URL
OAuth2AuthURL() string
OAuth2AuthURL() *url.URL
OAuth2TokenURL() *url.URL
OAuth2ClientRegistrationURL() *url.URL
JWKSURL() *url.URL
AllowTLSTerminationFrom() []string
AccessTokenStrategy() string
SubjectIdentifierAlgorithmSalt() string
Expand Down
19 changes: 15 additions & 4 deletions driver/configuration/provider_viper.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type ViperProvider struct {
const (
ViperKeyWellKnownKeys = "webfinger.jwks.broadcast_keys"
ViperKeyOAuth2ClientRegistrationURL = "webfinger.oidc_discovery.client_registration_url"
ViperKLeyOAuth2TokenURL = "webfinger.oidc_discovery.token_url"
ViperKLeyOAuth2AuthURL = "webfinger.oidc_discovery.auth_url"
ViperKeyJWKSURL = "webfinger.oidc_discovery.jwks_url"
ViperKeyOIDCDiscoverySupportedClaims = "webfinger.oidc_discovery.supported_claims"
ViperKeyOIDCDiscoverySupportedScope = "webfinger.oidc_discovery.supported_scope"
ViperKeyOIDCDiscoveryUserinfoEndpoint = "webfinger.oidc_discovery.userinfo_url"
Expand Down Expand Up @@ -446,14 +449,22 @@ func (v *ViperProvider) IssuerURL() *url.URL {
return urlRoot(urlx.ParseOrFatal(v.l, strings.TrimRight(viperx.GetString(v.l, ViperKeyIssuerURL, v.fallbackURL("/", v.publicHost(), v.publicPort()), "OAUTH2_ISSUER_URL", "ISSUER", "ISSUER_URL"), "/")+"/"))
}

func (v *ViperProvider) OAuth2AuthURL() string {
return "/oauth2/auth" // this should not have the host etc prepended...
}

func (v *ViperProvider) OAuth2ClientRegistrationURL() *url.URL {
return urlx.ParseOrFatal(v.l, viperx.GetString(v.l, ViperKeyOAuth2ClientRegistrationURL, "", "OAUTH2_CLIENT_REGISTRATION_URL"))
}

func (v *ViperProvider) OAuth2TokenURL() *url.URL {
return urlx.ParseOrFatal(v.l, viperx.GetString(v.l, ViperKLeyOAuth2TokenURL, urlx.AppendPaths(v.IssuerURL(), "/oauth2/token").String()))
}

func (v *ViperProvider) OAuth2AuthURL() *url.URL {
return urlx.ParseOrFatal(v.l, viperx.GetString(v.l, ViperKLeyOAuth2AuthURL, urlx.AppendPaths(v.IssuerURL(), "/oauth2/auth").String()))
}

func (v *ViperProvider) JWKSURL() *url.URL {
return urlx.ParseOrFatal(v.l, viperx.GetString(v.l, ViperKeyJWKSURL, urlx.AppendPaths(v.IssuerURL(), "/.well-known/jwks.json").String()))
}

func (v *ViperProvider) AllowTLSTerminationFrom() []string {
return viperx.GetStringSlice(v.l, ViperKeyAllowTLSTerminationFrom, []string{}, "HTTPS_ALLOW_TERMINATION_FROM")
}
Expand Down
3 changes: 3 additions & 0 deletions driver/configuration/provider_viper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ func TestViperProviderValidates(t *testing.T) {
// webfinger
assert.Equal(t, []string{"hydra.openid.id-token"}, c.WellKnownKeys())
assert.Equal(t, urlx.ParseOrPanic("https://example.com"), c.OAuth2ClientRegistrationURL())
assert.Equal(t, urlx.ParseOrPanic("https://example.com/jwks.json"), c.JWKSURL())
assert.Equal(t, urlx.ParseOrPanic("https://example.com/auth"), c.OAuth2AuthURL())
assert.Equal(t, urlx.ParseOrPanic("https://example.com/token"), c.OAuth2TokenURL())
assert.Equal(t, []string{"sub", "username"}, c.OIDCDiscoverySupportedClaims())
assert.Equal(t, []string{"offline_access", "offline", "openid", "whatever"}, c.OIDCDiscoverySupportedScope())
assert.Equal(t, "https://example.com", c.OIDCDiscoveryUserinfoEndpoint())
Expand Down
3 changes: 3 additions & 0 deletions internal/.hydra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ webfinger:
broadcast_keys:
- hydra.openid.id-token
oidc_discovery:
jwks_url: https://example.com/jwks.json
auth_url: https://example.com/auth
token_url: https://example.com/token
client_registration_url: https://example.com
supported_claims:
- username
Expand Down
30 changes: 16 additions & 14 deletions oauth2/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ func (h *Handler) SetRoutes(admin *x.RouterAdmin, public *x.RouterPublic, corsMi
public.GET(LogoutPath, h.LogoutHandler)
public.POST(LogoutPath, h.LogoutHandler)

public.GET(DefaultLoginPath, h.fallbackHandler("", "", http.StatusInternalServerError, configuration.ViperKeyLoginURL))
public.GET(DefaultConsentPath, h.fallbackHandler("", "", http.StatusInternalServerError, configuration.ViperKeyConsentURL))
public.GET(DefaultLogoutPath, h.fallbackHandler("", "", http.StatusInternalServerError, configuration.ViperKeyLogoutURL))
public.GET(DefaultLoginPath, h.fallbackHandler("", "", http.StatusOK, configuration.ViperKeyLoginURL))
public.GET(DefaultConsentPath, h.fallbackHandler("", "", http.StatusOK, configuration.ViperKeyConsentURL))
public.GET(DefaultLogoutPath, h.fallbackHandler("", "", http.StatusOK, configuration.ViperKeyLogoutURL))
public.GET(DefaultPostLogoutPath, h.fallbackHandler(
"You logged out successfully!",
"The Default Post Logout URL is not set which is why you are seeing this fallback page. Your log out request however succeeded.",
Expand Down Expand Up @@ -224,9 +224,9 @@ func (h *Handler) LogoutHandler(w http.ResponseWriter, r *http.Request, ps httpr
func (h *Handler) WellKnownHandler(w http.ResponseWriter, r *http.Request) {
h.r.Writer().Write(w, r, &WellKnown{
Issuer: strings.TrimRight(h.c.IssuerURL().String(), "/") + "/",
AuthURL: urlx.AppendPaths(h.c.IssuerURL(), AuthPath).String(),
TokenURL: urlx.AppendPaths(h.c.IssuerURL(), TokenPath).String(),
JWKsURI: urlx.AppendPaths(h.c.IssuerURL(), JWKPath).String(),
AuthURL: h.c.OAuth2AuthURL().String(),
TokenURL: h.c.OAuth2TokenURL().String(),
JWKsURI: h.c.JWKSURL().String(),
RevocationEndpoint: urlx.AppendPaths(h.c.IssuerURL(), RevocationPath).String(),
RegistrationEndpoint: h.c.OAuth2ClientRegistrationURL().String(),
SubjectTypes: h.c.SubjectTypesSupported(),
Expand Down Expand Up @@ -277,7 +277,7 @@ func (h *Handler) UserinfoHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
rfcerr := fosite.ErrorToRFC6749Error(err)
if rfcerr.StatusCode() == http.StatusUnauthorized {
w.Header().Set("WWW-Authenticate", fmt.Sprintf("error=%s,error_description=%s,error_hint=%s", rfcerr.Name, rfcerr.Description, rfcerr.Hint))
w.Header().Set("WWW-Authenticate", fmt.Sprintf("error=%s,error_description=%s,error_hint=%s", rfcerr.ErrorField, rfcerr.DescriptionField, rfcerr.HintField))
}
h.r.Writer().WriteError(w, r, err)
return
Expand All @@ -292,7 +292,7 @@ func (h *Handler) UserinfoHandler(w http.ResponseWriter, r *http.Request) {

c, ok := ar.GetClient().(*client.Client)
if !ok {
h.r.Writer().WriteError(w, r, errors.WithStack(fosite.ErrServerError.WithHint("Unable to type assert to *client.Client")))
h.r.Writer().WriteError(w, r, errors.WithStack(fosite.ErrServerError.WithHint("Unable to type assert to *client.Client.")))
return
}

Expand Down Expand Up @@ -341,7 +341,7 @@ func (h *Handler) UserinfoHandler(w http.ResponseWriter, r *http.Request) {

h.r.Writer().Write(w, r, interim)
} else {
h.r.Writer().WriteError(w, r, errors.WithStack(fosite.ErrServerError.WithHint(fmt.Sprintf("Unsupported userinfo signing algorithm \"%s\"", c.UserinfoSignedResponseAlg))))
h.r.Writer().WriteError(w, r, errors.WithStack(fosite.ErrServerError.WithHintf("Unsupported userinfo signing algorithm '%s'.", c.UserinfoSignedResponseAlg)))
return
}
}
Expand Down Expand Up @@ -437,7 +437,8 @@ func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request, _ ht
resp := &fosite.IntrospectionResponse{
Active: true,
AccessRequester: ar,
TokenType: tt,
TokenUse: tt,
AccessTokenType: "Bearer",
}

exp := resp.GetAccessRequester().GetSession().GetExpiresAt(tt)
Expand Down Expand Up @@ -475,7 +476,8 @@ func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request, _ ht
Audience: resp.GetAccessRequester().GetGrantedAudience(),
Issuer: strings.TrimRight(h.c.IssuerURL().String(), "/") + "/",
ObfuscatedSubject: obfuscated,
TokenType: string(resp.GetTokenType()),
TokenType: resp.GetAccessTokenType(),
TokenUse: string(resp.GetTokenUse()),
}); err != nil {
x.LogError(r, errors.WithStack(err), h.r.Logger())
}
Expand Down Expand Up @@ -739,10 +741,10 @@ func (h *Handler) writeAuthorizeError(w http.ResponseWriter, r *http.Request, ar

func (h *Handler) forwardError(w http.ResponseWriter, r *http.Request, err error) {
rfErr := fosite.ErrorToRFC6749Error(err)
query := url.Values{"error": {rfErr.Name}, "error_description": {rfErr.Description}, "error_hint": {rfErr.Hint}}
query := url.Values{"error": {rfErr.ErrorField}, "error_description": {rfErr.DescriptionField}, "error_hint": {rfErr.HintField}}

if h.c.ShareOAuth2Debug() {
query.Add("error_debug", rfErr.Debug)
query.Add("error_debug", rfErr.DebugField)
}

http.Redirect(w, r, urlx.CopyWithQuery(h.c.ErrorURL(), query).String(), http.StatusFound)
Expand All @@ -767,7 +769,7 @@ func (h *Handler) DeleteHandler(w http.ResponseWriter, r *http.Request, _ httpro
client := r.URL.Query().Get("client_id")

if client == "" {
h.r.Writer().WriteError(w, r, errors.WithStack(fosite.ErrInvalidRequest.WithHint(`Query parameter "client" is not defined but it should have been.`)))
h.r.Writer().WriteError(w, r, errors.WithStack(fosite.ErrInvalidRequest.WithHint(`Query parameter 'client' is not defined but it should have been.`)))
return
}

Expand Down
6 changes: 3 additions & 3 deletions oauth2/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,9 +447,9 @@ func TestHandlerWellKnown(t *testing.T) {

trueConfig := oauth2.WellKnown{
Issuer: strings.TrimRight(conf.IssuerURL().String(), "/") + "/",
AuthURL: urlx.AppendPaths(conf.IssuerURL(), oauth2.AuthPath).String(),
TokenURL: urlx.AppendPaths(conf.IssuerURL(), oauth2.TokenPath).String(),
JWKsURI: urlx.AppendPaths(conf.IssuerURL(), oauth2.JWKPath).String(),
AuthURL: conf.OAuth2AuthURL().String(),
TokenURL: conf.OAuth2TokenURL().String(),
JWKsURI: conf.JWKSURL().String(),
RevocationEndpoint: urlx.AppendPaths(conf.IssuerURL(), oauth2.RevocationPath).String(),
RegistrationEndpoint: conf.OAuth2ClientRegistrationURL().String(),
SubjectTypes: []string{"pairwise", "public"},
Expand Down

0 comments on commit bb8b982

Please sign in to comment.