Use additionalScopes for Okta auth instead of overriding scope entirely
Signed-off-by: ataylorme <andrew@ataylor.me>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
'@backstage/plugin-auth-backend': minor
|
||||
---
|
||||
|
||||
Allow user-defined scopes for Okta auth in config yaml
|
||||
Allow additional user-defined scopes for Okta auth in config yaml
|
||||
|
||||
Example `app-config.yaml` excerpt
|
||||
|
||||
@@ -18,11 +18,11 @@ auth:
|
||||
authServerId: ${AUTH_OKTA_AUTH_SERVER_ID} # Optional
|
||||
idp: ${AUTH_OKTA_IDP} # Optional
|
||||
# https://developer.okta.com/docs/reference/api/oidc/#scope-dependent-claims-not-always-returned
|
||||
scope: openid profile email offline_access groups # Optional
|
||||
additionalScopes: groups # Optional
|
||||
```
|
||||
|
||||
- Accept a new scope option during okta creation with `createAuthProviderIntegration`
|
||||
- Pass the user-defined `scope` as an option to `OktaAuthProvider`
|
||||
- Add `scope` as an option for `OktaAuthProvider`
|
||||
- Set `scope` in `OktaAuthProvider` to the `scope` passed as an `option` or a default of `'openid email profile offline_access'` if a user-defined option is not provided
|
||||
- Update the `start` and `refresh` methods to use `scope` from `OktaAuthProvider` rather than `scope` from the request
|
||||
- Accept a new additionalScope option during okta creation with `createAuthProviderIntegration`
|
||||
- Passes the the user-defined `additionalScopes` as an option to `OktaAuthProvider`
|
||||
- Add `additionalScopes` as an option for `OktaAuthProvider`
|
||||
- Set `scope` in `OktaAuthProvider` to the combined value of current scopes combined with the user-defined `additionalScopes` passed as an `option`
|
||||
- Update the `start` and `refresh` methods to use the new combiend `scope` from `OktaAuthProvider` rather than `scope` from the request
|
||||
|
||||
@@ -44,7 +44,7 @@ auth:
|
||||
authServerId: ${AUTH_OKTA_AUTH_SERVER_ID} # Optional
|
||||
idp: ${AUTH_OKTA_IDP} # Optional
|
||||
# https://developer.okta.com/docs/reference/api/oidc/#scope-dependent-claims-not-always-returned
|
||||
scope: ${AUTH_OKTA_SCOPE} # Optional
|
||||
additionalScopes: ${AUTH_OKTA_ADDITIONAL_SCOPES} # Optional
|
||||
```
|
||||
|
||||
The values referenced are found on the Application page on your Okta site.
|
||||
@@ -57,7 +57,7 @@ The values referenced are found on the Application page on your Okta site.
|
||||
- `authServerId`: The authorization server ID for the Application
|
||||
- `idp`: The identity provider for the application, e.g. `0oaulob4BFVa4zQvt0g3`
|
||||
|
||||
`scope` is an optional value to change the `scope` value sent to Okta during OAuth. The default value is `openid profile email offline_access`. Changing the `scope` will have an impact on [the dependent claims returned](https://developer.okta.com/docs/reference/api/oidc/#scope-dependent-claims-not-always-returned). For example, adding `groups` to the `scope` by setting the value to `openid profile email offline_access groups` will result in the claim returning a list of the groups that the user is a member of that also match the ID token group filter of the client app.
|
||||
`additionalScopes` is an optional value, a string of space separated scopes, that will be combined with the default `scope` value of `openid profile email offline_access` to adjust the `scope` sent to Okta during OAuth. This will have an impact on [the dependent claims returned](https://developer.okta.com/docs/reference/api/oidc/#scope-dependent-claims-not-always-returned). For example, setting the `additionalScopes` value to `groups` will result in the claim returning a list of the groups that the user is a member of that also match the ID token group filter of the client app.
|
||||
|
||||
## Adding the provider to the Backstage frontend
|
||||
|
||||
|
||||
Vendored
+1
@@ -131,6 +131,7 @@ export interface Config {
|
||||
authServerId?: string;
|
||||
idp?: string;
|
||||
callbackUrl?: string;
|
||||
additionalScopes?: string;
|
||||
};
|
||||
};
|
||||
/** @visibility frontend */
|
||||
|
||||
@@ -105,6 +105,7 @@ describe('createOktaProvider', () => {
|
||||
});
|
||||
|
||||
it('should pass a custom scope to start and refresh requests', async () => {
|
||||
const additionalScopes = 'groups';
|
||||
const mockScope = 'openid profile email offline_access groups';
|
||||
const reqScope = 'openid profile email offline_access';
|
||||
const provider = new OktaAuthProvider({
|
||||
@@ -119,7 +120,7 @@ describe('createOktaProvider', () => {
|
||||
clientId: 'mock',
|
||||
clientSecret: 'mock',
|
||||
callbackUrl: 'mock',
|
||||
scope: mockScope,
|
||||
additionalScopes,
|
||||
});
|
||||
|
||||
mockRedirectStrategy.mockResolvedValueOnce({
|
||||
|
||||
@@ -60,7 +60,7 @@ export type OktaAuthProviderOptions = OAuthProviderOptions & {
|
||||
signInResolver?: SignInResolver<OAuthResult>;
|
||||
authHandler: AuthHandler<OAuthResult>;
|
||||
resolverContext: AuthResolverContext;
|
||||
scope?: string;
|
||||
additionalScopes?: string;
|
||||
};
|
||||
|
||||
export class OktaAuthProvider implements OAuthHandlers {
|
||||
@@ -68,7 +68,7 @@ export class OktaAuthProvider implements OAuthHandlers {
|
||||
private readonly signInResolver?: SignInResolver<OAuthResult>;
|
||||
private readonly authHandler: AuthHandler<OAuthResult>;
|
||||
private readonly resolverContext: AuthResolverContext;
|
||||
private readonly scope: string;
|
||||
private readonly additionalScopes: string;
|
||||
|
||||
/**
|
||||
* Due to passport-okta-oauth forcing options.state = true,
|
||||
@@ -91,7 +91,7 @@ export class OktaAuthProvider implements OAuthHandlers {
|
||||
this.signInResolver = options.signInResolver;
|
||||
this.authHandler = options.authHandler;
|
||||
this.resolverContext = options.resolverContext;
|
||||
this.scope = options.scope || 'openid email profile offline_access';
|
||||
this.additionalScopes = options.additionalScopes || '';
|
||||
|
||||
this.strategy = new OktaStrategy(
|
||||
{
|
||||
@@ -128,11 +128,19 @@ export class OktaAuthProvider implements OAuthHandlers {
|
||||
);
|
||||
}
|
||||
|
||||
private combineScopeStrings(scopesA: string, scopesB: string) {
|
||||
const scopesAArray = scopesA.split(' ');
|
||||
const scopesBArray = scopesB.split(' ');
|
||||
const combinedScopes = new Set([...scopesAArray, ...scopesBArray]);
|
||||
return Array.from(combinedScopes).join(' ');
|
||||
}
|
||||
|
||||
async start(req: OAuthStartRequest): Promise<OAuthStartResponse> {
|
||||
const scope = this.combineScopeStrings(req.scope, this.additionalScopes);
|
||||
return await executeRedirectStrategy(req, this.strategy, {
|
||||
accessType: 'offline',
|
||||
prompt: 'consent',
|
||||
scope: this.scope,
|
||||
scope: scope,
|
||||
state: encodeState(req.state),
|
||||
});
|
||||
}
|
||||
@@ -150,12 +158,9 @@ export class OktaAuthProvider implements OAuthHandlers {
|
||||
}
|
||||
|
||||
async refresh(req: OAuthRefreshRequest) {
|
||||
const scope = this.combineScopeStrings(req.scope, this.additionalScopes);
|
||||
const { accessToken, refreshToken, params } =
|
||||
await executeRefreshTokenStrategy(
|
||||
this.strategy,
|
||||
req.refreshToken,
|
||||
this.scope,
|
||||
);
|
||||
await executeRefreshTokenStrategy(this.strategy, req.refreshToken, scope);
|
||||
|
||||
const fullProfile = await executeFetchUserProfileStrategy(
|
||||
this.strategy,
|
||||
@@ -233,7 +238,8 @@ export const okta = createAuthProviderIntegration({
|
||||
const callbackUrl =
|
||||
customCallbackUrl ||
|
||||
`${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
||||
const scope = envConfig.getOptionalString('scope');
|
||||
const additionalScopes =
|
||||
envConfig.getOptionalString('additionalScopes');
|
||||
|
||||
// This is a safe assumption as `passport-okta-oauth` uses the audience
|
||||
// as the base for building the authorization, token, and user info URLs.
|
||||
@@ -258,7 +264,7 @@ export const okta = createAuthProviderIntegration({
|
||||
authHandler,
|
||||
signInResolver: options?.signIn?.resolver,
|
||||
resolverContext,
|
||||
scope,
|
||||
additionalScopes,
|
||||
});
|
||||
|
||||
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
||||
|
||||
Reference in New Issue
Block a user