-
-
Notifications
You must be signed in to change notification settings - Fork 92
Description
There are probably quite a few parts in this issue, I totally see those as separate small PRs / issues.
Context
Currently, auth package supports various OAuth2 providers and acts as a OAuth2 Authorization Server / proxy (without strongly committing to RFC) by issuing self-signed ID tokens based on info provided by /userinfo response, which are passed via a cookie, with extra XSRF protection.
Proposal
Support OpenID providers and ID tokens issued by an external identity provider as authentication tokens. In this scenario, the server won't have private keys and won't issue ID tokens itself. Potentially support OpenID Connect Discovery protocol. Unlike general OAuth2 spec, OpenID supports issuing signed ID tokens bound to a particular user, so these tokens can be used for authentication.
How I see the potential configuration:
// code below will fetch OpenID Connect Discovery config
// from https://accounts.google.com/.well-known/openid-configuration
// then it'll load keys from "jwks_uri" endpoint and store them under "google" provider
service.AddOpenIDProvider("google", "https://accounts.google.com", "client-id", "client-secret")
m := service.Middleware()
router := chi.NewRouter()
// setup auth routes
authRoutes, avaRoutes := service.Handlers()
// for OpenID providers, /auth handler won't just fetch access token + userinfo,
// but will actually request a signed ID token from the provider.
// This token can be then passed to the client as is, as it is signed already
// and we have public keys for validate it.
router.Mount("/auth", authRoutes) // add auth handlers
router.Mount("/avatar", avaRoutes) // add avatar handler
// m.OpenIDAuth won't use own keys defined in opts.SecretReader, but instead will use public keys loaded before
// the provider can be identified either by "iss" claim or manually configured, i.e. m.OpenIDAuth("google")
// Also, OpenIDAuth shouldn't require a cookie, but Authorization: Bearer <jwt> header to work.
router.With(m.OpenIDAuth).Get("/api-endpoint", protectedRouteHandler) // protected api
router.With(m.OpenIDAuth(func(claims token.Claims) bool {
// do any authorization checks on the token claims, e.g. Role
})).Get("/api-endpoint", protectedRouteHandler) // protected apiWith OpenIDAuth authentication middleware, the server should act as a OAuth2 Resource Server, i.e. to expect Authorization: Bearer <jwt> header and use that for authentication.
Having this would allow:
- Remove need in issuing auth (ID) tokens, as well as managing signing keys
- Easy integration with new providers
- Unify user / API authentication middleware (by using
Authorizationheader) - Multiple services using the same auth middleware configuration without sharing secrets
Considerations:
- Some parts of configuration options are irrelevant for OpenID providers, where
authis only acting as a authentication proxy basically. Likeopts.ClaimsUpdaterwon't work - since we don't have private keys, we can't modify claims. Probably OpenID should have own configuration options? - How to pass a signed ID token to the client? I think the cookie approach can still be used, but instead of checking the cookie,
Authorizationheader should be checked, which is set from the cookie. This kind of gives XSRF protection for free.