Skip to content
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

chore: add additional OIDC auth resolvers #2020

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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 @@ -60,6 +60,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"
},
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 @@ -22496,6 +22496,7 @@ __metadata:
"@types/global-agent": 2.1.3
app: "*"
global-agent: 3.0.0
jose: ^5.9.6
prettier: 3.4.2
undici: 6.19.8
winston: 3.14.2
Expand Down Expand Up @@ -32478,6 +32479,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
Loading