pilcrowonpaper / oslo

A collection of auth-related utilities
https://oslo.js.org
MIT License
1.06k stars 35 forks source link

Support for OAuth2 endpoint discovery #44

Closed marcomuser closed 5 months ago

marcomuser commented 8 months ago

First of all, thanks for this fantastic library! It would be great if one could only specify the .well-known discovery endpoint pointing to the server's metadata document (https://datatracker.ietf.org/doc/html/rfc8414) when instantiating the OAuth2Client and let it figure out the authorizeEndpoint and the tokenEndpoint automatically when sending requests.

pilcrowonpaper commented 6 months ago

We could probably add an API that parses the well known config, but I'm not sure how it could automatically configure PKCE since that's defined within createAuthorizationURL() instead of the entire OAuth2Client class

marcomuser commented 5 months ago

Yes, that's true. What was the reason to export a separate function createAuthorizationURL instead of exposing it as a class method?

pilcrowonpaper commented 5 months ago

It is a class method?

marcomuser commented 5 months ago

Yeaaaah, sorry about that. I shouldn't post from my phone late in the evening. 😆 Was thinking of something else.

We could focus on automatically discovering and configuring the /token and /authorize endpoints via the .well-known discovery endpoint IF a user instantiates the client without token and authorize endpoints BUT provides the discovery endpoint. This is how it works in the oauth2-client lib. The createAuthorizationURL method would still expect its arguments as-is for PKCE. The benefit would be that the client would continue to function just fine if the oAuth2 server decided - for whatever reason - to change the urls of either token or authorize endpoint.

Let me know if I'm still missing your point. ;-) Very well possible.

pilcrowonpaper commented 5 months ago

Sorry, got Oslo and Arctic providers mixed up.

Honestly though, If you know the well known endpoint, I feel it's easier if you just copy paste the authorization and token endpoint. It would be nice if Oslo could parse the well known config and magically update the types to require certain items, but I don't think that would be realistic.

marcomuser commented 5 months ago

What do you mean by magically updating the types? If you are talking about the client instantiation, this could simply be handled by a union type if the arguments were all passed in a single object:

new OAuth2Client({
    clientId,
    authorizeEndpoint
    tokenEndpoint,
    redirectURI
});

OR

new OAuth2Client({
    clientId,
    discoveryEndpoint,
    redirectURI
});

The Options type would look something like this:

type OAuth2ClientOptions = {
    clientId: string,
    authorizeEndpoint: string,
    tokenEndpoint: string,
    redirectURI: string
} | {
    clientId: string,
    discoveryEndpoint: string,
    redirectURI: string
}

It would force the user to either be explicit with the endpoints or let the client handle it via parsing the well-known config.

Having said that, I recognize that the additional fetching logic and parsing of the well-known config adds quite a bit of complexity. The benefit as said earlier is that it allows OAuth Providers to change the endpoints without breaking clients downstream. If you decide that that is not worth the added complexity I'm absolutely fine with that! This library is fantastic already as-is so thanks again for it!

pilcrowonpaper commented 5 months ago

By magically, I mean like requiring PKCE for providers that support it etc.

RaphaelBossek commented 2 months ago

I'm a little bit shocked how this proposal/disussion was shutdown here.

First I've setup my Keycloak to use PKCE and checked the /.well-known/openid-configuration for my realm and as described in https://www.rfc-editor.org/rfc/rfc8414.html the metadata name code_challenge_methods_supported can be used to identify which challenage method is supported by PKCE so PKCE is enabled and can be used for configuration.

Here a corresponding setup: { "issuer": "https://auth.local.str84ward.net/realms/master", "authorization_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/auth", "token_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/token", "introspection_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/token/introspect", "userinfo_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/userinfo", "end_session_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/logout", "frontchannel_logout_session_supported": true, "frontchannel_logout_supported": true, "jwks_uri": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/certs", "check_session_iframe": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/login-status-iframe.html", "grant_types_supported": [ "authorization_code", "implicit", "refresh_token", "password", "client_credentials", "urn:openid:params:grant-type:ciba", "urn:ietf:params:oauth:grant-type:device_code" ], "acr_values_supported": [ "0", "1" ], "response_types_supported": [ "code", "none", "id_token", "token", "id_token token", "code id_token", "code token", "code id_token token" ], "subject_types_supported": [ "public", "pairwise" ], "id_token_signing_alg_values_supported": [ "PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512" ], "id_token_encryption_alg_values_supported": [ "RSA-OAEP", "RSA-OAEP-256", "RSA1_5" ], "id_token_encryption_enc_values_supported": [ "A256GCM", "A192GCM", "A128GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512" ], "userinfo_signing_alg_values_supported": [ "PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512", "none" ], "userinfo_encryption_alg_values_supported": [ "RSA-OAEP", "RSA-OAEP-256", "RSA1_5" ], "userinfo_encryption_enc_values_supported": [ "A256GCM", "A192GCM", "A128GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512" ], "request_object_signing_alg_values_supported": [ "PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512", "none" ], "request_object_encryption_alg_values_supported": [ "RSA-OAEP", "RSA-OAEP-256", "RSA1_5" ], "request_object_encryption_enc_values_supported": [ "A256GCM", "A192GCM", "A128GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512" ], "response_modes_supported": [ "query", "fragment", "form_post", "query.jwt", "fragment.jwt", "form_post.jwt", "jwt" ], "registration_endpoint": "https://auth.local.str84ward.net/realms/master/clients-registrations/openid-connect", "token_endpoint_auth_methods_supported": [ "private_key_jwt", "client_secret_basic", "client_secret_post", "tls_client_auth", "client_secret_jwt" ], "token_endpoint_auth_signing_alg_values_supported": [ "PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512" ], "introspection_endpoint_auth_methods_supported": [ "private_key_jwt", "client_secret_basic", "client_secret_post", "tls_client_auth", "client_secret_jwt" ], "introspection_endpoint_auth_signing_alg_values_supported": [ "PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512" ], "authorization_signing_alg_values_supported": [ "PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512" ], "authorization_encryption_alg_values_supported": [ "RSA-OAEP", "RSA-OAEP-256", "RSA1_5" ], "authorization_encryption_enc_values_supported": [ "A256GCM", "A192GCM", "A128GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512" ], "claims_supported": [ "aud", "sub", "iss", "auth_time", "name", "given_name", "family_name", "preferred_username", "email", "acr" ], "claim_types_supported": [ "normal" ], "claims_parameter_supported": true, "scopes_supported": [ "openid", "address", "acr", "profile", "offline_access", "microprofile-jwt", "phone", "roles", "web-origins", "email" ], "request_parameter_supported": true, "request_uri_parameter_supported": true, "require_request_uri_registration": true, "code_challenge_methods_supported": [ "plain", "S256" ], "tls_client_certificate_bound_access_tokens": true, "revocation_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/revoke", "revocation_endpoint_auth_methods_supported": [ "private_key_jwt", "client_secret_basic", "client_secret_post", "tls_client_auth", "client_secret_jwt" ], "revocation_endpoint_auth_signing_alg_values_supported": [ "PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512" ], "backchannel_logout_supported": true, "backchannel_logout_session_supported": true, "device_authorization_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/auth/device", "backchannel_token_delivery_modes_supported": [ "poll", "ping" ], "backchannel_authentication_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/ext/ciba/auth", "backchannel_authentication_request_signing_alg_values_supported": [ "PS384", "RS384", "EdDSA", "ES384", "ES256", "RS256", "ES512", "PS256", "PS512", "RS512" ], "require_pushed_authorization_requests": false, "pushed_authorization_request_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/ext/par/request", "mtls_endpoint_aliases": { "token_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/token", "revocation_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/revoke", "introspection_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/token/introspect", "device_authorization_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/auth/device", "registration_endpoint": "https://auth.local.str84ward.net/realms/master/clients-registrations/openid-connect", "userinfo_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/userinfo", "pushed_authorization_request_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/ext/par/request", "backchannel_authentication_endpoint": "https://auth.local.str84ward.net/realms/master/protocol/openid-connect/ext/ciba/auth" }, "authorization_response_iss_parameter_supported": true }