-
Notifications
You must be signed in to change notification settings - Fork 8
feat: add external IDP integration #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@vbelouso This looks stunning! |
| quarkus.http.auth.permission.authenticated.policy=authenticated | ||
| # --- PROFILE: external-idp (External Identity Provider) --- | ||
| # Supports: Keycloak (standalone or broker), GitHub, Google, Auth0, etc. | ||
| %external-idp.quarkus.oidc.application-type=web-app |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vbelouso This one mean that only authorization code flow auth will be performed in the browser for the front-end web side, but not authentication using token/jwt on the rest resources' endpoints ( like now in prod profile, that you needs a jwt access token to invoke the service or the route of the rest endpoints from the outside).
The question is, do we want that or not?
IMO, we should at least keep the situation like it was before ( only authentication for rest API endpoints ( not authorization)) using a permanent JWT generated by the auth service that never expires or expires within a predefined period, and then we'll use it as a secret in the client deployment , Or we can take it to the next level and let the user/consumer of the api endpoints to pass the auth bearer JWT token themselves ( after they authenticated in front of auth service/keycloak' token endpoint themselves /realms/<realm-name>/protocol/openid-connect/token and got a JWT access token), but we'll need to revert it the oidc application type also to hybrid.
instead of it, Another option is that we can also revert it to hybrid back, and enable permissions based on scopes or just based on roles that the users having , we should think about that.
We can lookup the various options at the relevant references:
https://quarkus.io/guides/security-oidc-bearer-token-authentication
https://quarkus.io/guides/security-oidc-bearer-token-authentication-tutorial
Also we can take it one step ahead and manage all permissions in keycloak as authorization service( for external idp's only if they're supporting authorization services ( permissions , roles , client scopes, etc.):
https://quarkus.io/guides/security-keycloak-authorization.
For that maybe we can think for another profile of keycloak alone, and the external-idp profile can remain for keycloak broker (delegates authentication to external idps) and for direct external idps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vbelouso This is fantastic job you've done - still need to test it, but probably will have time only on sunday ( most of the things looks right)
Only 2 things are missing, and one comment above about bearer-token-authentication for rest api endpoints :
- When building the native image, it's being built with default profile (
prod), and hence afterwards, when setting/passing at runtime a property of different profile or overriding env var ofQUARKUS_PROFILE, then if the runtime profile is different, it might cause problems and shock stability, So you need to add to documentation that when building an executable ( native or JVM Jar one), regardless if it's in the command line or in the Various Dockerfiles variants, to add the build time property to the build process. for example , in:
https://github.com/RHEcosystemAppEng/agent-morpheus-client/blob/86eaa51017fdf86af8280cf0401aee27ee5870e9/docs/development.md#creating-a-native-executable
You should add the build time property of the new profile so it will work correctly without side effects.
./mvnw package -Dnative -Dquarkus.profile=external-idp
- The Bean
JkwsRequestFiltershould be scoped only to the profile ofprod, as the under the new profile, thequarkus.oidc.jwks-pathproperty is not set, hence this bean is redundant and should be annotated with annotation@IfBuildProfile
Lines 19 to 49 in 86eaa51
| @ApplicationScoped | |
| @OidcEndpoint(value = Type.JWKS) | |
| @Unremovable | |
| public class JkwsRequestFilter implements OidcRequestFilter { | |
| private static final Logger LOGGER = Logger.getLogger(JkwsRequestFilter.class); | |
| private static final Path SA_TOKEN_PATH = Path.of("/var/run/secrets/kubernetes.io/serviceaccount/token"); | |
| Optional<String> token = Optional.empty(); | |
| @PostConstruct | |
| void loadToken() { | |
| if (Files.exists(SA_TOKEN_PATH)) { | |
| try { | |
| token = Optional.of(Files.readString(SA_TOKEN_PATH)); | |
| } catch (IOException e) { | |
| LOGGER.errorf("Unable to read Service Account Token: %s", SA_TOKEN_PATH.toString(), e); | |
| } | |
| } | |
| } | |
| @Override | |
| public void filter(OidcRequestContext requestContext) { | |
| HttpMethod method = requestContext.request().method(); | |
| String uri = requestContext.request().uri(); | |
| if (method == HttpMethod.GET && uri.endsWith("/jwks") && token.isPresent()) { | |
| requestContext.request().bearerTokenAuthentication(token.get()); | |
| } | |
| } | |
| } |
In addition, You can still use something similar for authentication and validation of JWT for every request , but then the suffix of the jwks endpoint of keycloak may be different, because you're using quarkus.oidc.discovery-enabled=true, then the jwks endpoint can be retrieved from realms/your-realm/.well-known/openid-configuration endpoint of the server ( and by default it's not ending with /jwks , in keycloak server, for instance, by default it's /realms/realm_name/protocol/openid-connect/certs)
In addition, in application-type=web-app ( and not hybrid) there will be no call to the jwks endpoint by the oidc client, ( not for bear token authentication + validation, only for requests of authorization code flow auth for authentication in the browser), So you need to implement an HTTP request filter, that will intercept all requests came from remote addresses ( not localhost, this will be for free), and then it will take the auth header' token value => Authorization: Bearer $TOKEN and validate it against the jwks endpoint of the auth-server(e.g keycloak), if it'll be validated and verified, then it will continue to the request handler, otherwise, there will be a response of 401 or 403 ( based on what returning from the realms/your-realm/.well-known/openid-configuration' jwks endpoint)
Then users/clients will need to take/get token generated in the auth server ( keycloak) and will use them in Authorization Header as bearer token.
But in order to let this logic/mechanism to only run in the new profile, then you'll need to have a special boolean property set on the level of the new profile name ( in application.properties), so it will not affect the dev/prod/test profiles ( and then if this property value is disabled , you just skip the filter logic and nothing happened).
Thanks for your efforts!.
Adds support for external OIDC providers (Keycloak, Google, etc) via new
external-idpprofile.Changes
Authentication:
external-idpprofile for external identity providersClear-Site-Dataheader (no IdP session termination)Documentation:
docs/authentication.md- comprehensive auth configuration guidedocs/configuration.mdanddocs/development.mdwith linksTesting:
scripts/test-auth.sh- automated testing for all auth scenarios