Skip to content

Commit

Permalink
add additional OIDC auth resolvers
Browse files Browse the repository at this point in the history
Signed-off-by: Jessica He <[email protected]>
  • Loading branch information
JessicaJHee committed Dec 3, 2024
1 parent 16746e2 commit 6ad8b29
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ For more information on setting up the OAuth2 Proxy auth provider, consult the [
# - resolver: preferredUsernameMatchingUserEntityName
# - resolver: emailMatchingUserEntityProfileEmail
# - resolver: emailLocalPartMatchingUserEntityName
# - resolver: oidcSubClaimMatchingKeycloakUserId
```

In an example using Keycloak for authentication with the OIDC provider, there are a few steps that need to be taken to get everything working:
Expand All @@ -122,6 +123,7 @@ In an example using Keycloak for authentication with the OIDC provider, there ar
The default resolver provided by the `oidc` auth provider is the `emailLocalPartMatchingUserEntityName` resolver.

If you want to use a different resolver, add the resolver you want to use in the `auth.providers.oidc.[environment].signIn.resolvers` configuration as soon in the example above, and it will override the default resolver.
* For enhanced security, consider using the `oidcSubClaimMatchingKeycloakUserId` resolver which matches the user with the immutable `sub` parameter from OIDC to the Keycloak user ID.

For more information on setting up the OIDC auth provider, consult the [Backstage documentation](https://backstage.io/docs/auth/oidc#the-configuration).

Expand Down
2 changes: 1 addition & 1 deletion docs/dynamic-plugins/installing-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
To install a dynamic plugin, you need to add the plugin definition to the `dynamic-plugin-config.yaml` file.

The placement of `dynamic-plugin-config.yaml` depends on the deployment method.
For more information, see [Installing Dynamic Plugins with the Red Hat Developer Hub Operator](https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.3/html/installing_and_viewing_dynamic_plugins/proc-config-dynamic-plugins-rhdh-operator_title-plugins-rhdh-about) or [Installing Dynamic Plugins Using the Helm Chart](https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.3/html/installing_and_viewing_dynamic_plugins/con-install-dynamic-plugin-helm_title-plugins-rhdh-about).
For more information, see [Installing Dynamic Plugins with the Red Hat Developer Hub Operator](https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.3/html/installing_and_viewing_dynamic_plugins/index#proc-config-dynamic-plugins-rhdh-operator_title-plugins-rhdh-about) or [Installing Dynamic Plugins Using the Helm Chart](https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.3/html/installing_and_viewing_dynamic_plugins/index#con-install-dynamic-plugin-helm_title-plugins-rhdh-about).

Plugins are defined in the `plugins` array in the `dynamic-plugin-config.yaml` file. Each plugin is defined as an object with the following properties:

Expand Down
4 changes: 4 additions & 0 deletions docs/ping-identity-oidc-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ auth:
clientId: ${PING_IDENTITY_CLIENT_ID}
clientSecret: ${PING_IDENTITY_CLIENT_SECRET}
prompt: auto #optional
signIn:
resolvers:
- resolver: oidcSubClaimMatchingPingIdentityUserId
```
The OIDC provider requires three mandatory configuration keys:
Expand All @@ -46,6 +49,7 @@ The OIDC provider requires three mandatory configuration keys:
- `metadataUrl`: Copy from `OIDC Discovery Endpoint` under `Configuration` tab in `URLs` drop down.
- `prompt` (optional): Recommended to use auto so the browser will request login to the IDP if the user has no active session.
- `additionalScopes` (optional): List of scopes for the App Registration, to be requested in addition to the required ones.
- `signIn.resolvers.resolver` (optional): `oidcSubClaimMatchingPingIdentityUserId` is a secure user resolver that matches the `sub` claim from OIDC to the Ping Identity user ID.

#### Known Issues

Expand Down
1 change: 1 addition & 0 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"@opentelemetry/sdk-node": "0.53.0",
"app": "*",
"global-agent": "3.0.0",
"jose": "^5.9.6",
"undici": "6.19.8",
"winston": "3.14.2",
"winston-daily-rotate-file": "5.0.0"
Expand Down
6 changes: 6 additions & 0 deletions packages/backend/src/modules/authProvidersModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
createOAuthProviderFactory,
} from '@backstage/plugin-auth-node';

import { rhdhSignInResolvers } from './authResolvers';

/**
* Function is responsible for signing in a user with the catalog user and
* creating an entity reference based on the provided name parameter.
Expand Down Expand Up @@ -221,6 +223,10 @@ function getAuthProviderFactory(providerId: string): AuthProviderFactory {
signInResolver:
oidcSignInResolvers.emailLocalPartMatchingUserEntityName(),
signInResolverFactories: {
oidcSubClaimMatchingKeycloakUserId:
rhdhSignInResolvers.oidcSubClaimMatchingKeycloakUserId,
oidcSubClaimMatchingPingIdentityUserId:
rhdhSignInResolvers.oidcSubClaimMatchingPingIdentityUserId,
...oidcSignInResolvers,
},
});
Expand Down
74 changes: 74 additions & 0 deletions packages/backend/src/modules/authResolvers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { OidcAuthResult } from '@backstage/plugin-auth-backend-module-oidc-provider';
import {
AuthResolverContext,
createSignInResolverFactory,
OAuthAuthenticatorResult,
SignInInfo,
} from '@backstage/plugin-auth-node';

import { decodeJwt } from 'jose';

const KEYCLOAK_ID_ANNOTATION = 'keycloak.org/id';
const PING_IDENTITY_ID_ANNOTATION = 'pingidentity.org/id';

/**
* Creates an OIDC sign-in resolver that looks up the user using a specific annotation key.
*
* @param annotationKey - The annotation key to match the user's `sub` claim.
* @param providerName - The name of the identity provider to report in error message if the `sub` claim is missing.
*/
const createOidcSubClaimResolver = (userIdKey: string, providerName: string) =>
createSignInResolverFactory({
create() {
return async (
info: SignInInfo<OAuthAuthenticatorResult<OidcAuthResult>>,
ctx: AuthResolverContext,
) => {
const sub = info.result.fullProfile.userinfo.sub;
if (!sub) {
throw new Error(
`The user profile from ${providerName} is missing a 'sub' claim, likely due to a misconfiguration in the provider. Please contact your system administrator for assistance.`,
);
}

const idToken = info.result.fullProfile.tokenset.id_token;
if (!idToken) {
throw new Error(
`The user ID token from ${providerName} is missing a 'sub' claim, likely due to a misconfiguration in the provider. Please contact your system administrator for assistance.`,
);
}

const subFromIdToken = decodeJwt(idToken)?.sub;
if (sub !== subFromIdToken) {
throw new Error(
`There was a problem verifying your identity with ${providerName} due to a mismatching 'sub' claim. Please contact your system administrator for assistance.`,
);
}

return ctx.signInWithCatalogUser({
annotations: { [userIdKey]: sub },
});
};
},
});

/**
* Additional sign-in resolvers for the Oidc auth provider.
*
* @public
*/
export namespace rhdhSignInResolvers {
/**
* An OIDC resolver that looks up the user using their Keycloak user ID.
*/
export const oidcSubClaimMatchingKeycloakUserId = createOidcSubClaimResolver(
KEYCLOAK_ID_ANNOTATION,
'Keycloak',
);

/**
* An OIDC resolver that looks up the user using their Ping Identity user ID.
*/
export const oidcSubClaimMatchingPingIdentityUserId =
createOidcSubClaimResolver(PING_IDENTITY_ID_ANNOTATION, 'Ping Identity');
}
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -22385,6 +22385,7 @@ __metadata:
"@types/global-agent": 2.1.3
app: "*"
global-agent: 3.0.0
jose: ^5.9.6
prettier: 3.4.1
undici: 6.19.8
winston: 3.14.2
Expand Down Expand Up @@ -32375,6 +32376,13 @@ __metadata:
languageName: node
linkType: hard

"jose@npm:^5.9.6":
version: 5.9.6
resolution: "jose@npm:5.9.6"
checksum: 4b536da0201858ed4c4582e8bb479081f11e0c63dd0f5e473adde16fc539785e1f2f0409bc1fc7cbbb5b68026776c960b4952da3a06f6fdfff0b9764c9127ae0
languageName: node
linkType: hard

"joycon@npm:^3.0.1":
version: 3.1.1
resolution: "joycon@npm:3.1.1"
Expand Down

0 comments on commit 6ad8b29

Please sign in to comment.