Merge pull request #16401 from jamieklassen/gitlab-oidc-k8s

GitLab auth provider gets ID tokens and be used as a k8s oidcTokenProvider
This commit is contained in:
Patrik Oldsberg
2023-03-07 15:39:00 +01:00
committed by GitHub
11 changed files with 76 additions and 15 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-kubernetes': patch
---
GitLab can now be used as an `oidcTokenProvider` for Kubernetes clusters
+19
View File
@@ -0,0 +1,19 @@
---
'@backstage/core-app-api': minor
---
`OAuth2` now gets ID tokens from a session with the `openid` scope explicitly
requested.
This should not be considered a breaking change, because spec-compliant OIDC
providers will already be returning ID tokens if and only if the `openid` scope
is granted.
This change makes the dependence explicit, and removes the burden on
OAuth2-based providers which require an ID token (e.g. this is done by various
default [auth
handlers](https://backstage.io/docs/auth/identity-resolver/#authhandler)) to add
`openid` to their default scopes. _That_ could carry another indirect benefit:
by removing `openid` from the default scopes for a provider, grants for
resource-specific access tokens can avoid requesting excess ID token-related
scopes.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-plugin-api': minor
---
The GitLab auth provider can now be used to get OpenID tokens.
+5 -1
View File
@@ -18,7 +18,11 @@ Settings for local development:
- Name: Backstage (or your custom app name)
- Redirect URI: `http://localhost:7007/api/auth/gitlab/handler/frame`
- Scopes: `read_api` and `read_user`
- Scopes: `read_user` for sign-in. If you also need ID tokens (e.g. if you are
using the Kubernetes plugin and have clusters with `authProvider: oidc` and
[`oidcTokenProvider:
gitlab`](https://backstage.io/docs/features/kubernetes/configuration/#clustersoidctokenprovider-optional)),
add the `openid` scope.
## Configuration
+3 -2
View File
@@ -160,8 +160,9 @@ auth:
audience: ${AUTH_OKTA_AUDIENCE}
```
The following values are supported out-of-the-box by the frontend: `google`, `microsoft`,
`okta`, `onelogin`.
The following values are supported out-of-the-box by the frontend: `gitlab` (the
application whose `clientId` is used by the auth provider should be granted the
`openid` scope), `google`, `microsoft`, `okta`, `onelogin`.
Take note that `oidcTokenProvider` is just the issuer for the token, you can use any
of these with an OIDC enabled cluster, like using `microsoft` as the issuer for a EKS
@@ -48,9 +48,8 @@ describe('OAuth2', () => {
expect(await oauth2.getAccessToken('my-scope my-scope2')).toBe(
'access-token',
);
expect(getSession).toHaveBeenCalledTimes(1);
expect(getSession.mock.calls[0][0].scopes).toEqual(
new Set(['my-scope', 'my-scope2']),
expect(getSession).toHaveBeenCalledWith(
expect.objectContaining({ scopes: new Set(['my-scope', 'my-scope2']) }),
);
});
@@ -65,9 +64,10 @@ describe('OAuth2', () => {
});
expect(await oauth2.getAccessToken('my-scope')).toBe('access-token');
expect(getSession).toHaveBeenCalledTimes(1);
expect(getSession.mock.calls[0][0].scopes).toEqual(
new Set(['my-prefix/my-scope']),
expect(getSession).toHaveBeenCalledWith(
expect.objectContaining({
scopes: new Set(['my-prefix/my-scope']),
}),
);
});
@@ -82,7 +82,11 @@ describe('OAuth2', () => {
});
expect(await oauth2.getIdToken()).toBe('id-token');
expect(getSession).toHaveBeenCalledTimes(1);
expect(getSession).toHaveBeenCalledWith(
expect.objectContaining({
scopes: new Set(['openid']),
}),
);
});
it('should get optional id token', async () => {
@@ -96,7 +100,11 @@ describe('OAuth2', () => {
});
expect(await oauth2.getIdToken({ optional: true })).toBe('id-token');
expect(getSession).toHaveBeenCalledTimes(1);
expect(getSession).toHaveBeenCalledWith(
expect.objectContaining({
scopes: new Set(['openid']),
}),
);
});
it('should share popup closed errors', async () => {
@@ -153,7 +153,10 @@ export default class OAuth2
}
async getIdToken(options: AuthRequestOptions = {}) {
const session = await this.sessionManager.getSession(options);
const session = await this.sessionManager.getSession({
...options,
scopes: new Set(['openid']),
});
return session?.providerInfo.idToken ?? '';
}
+5 -1
View File
@@ -482,7 +482,11 @@ export const githubAuthApiRef: ApiRef<
// @public
export const gitlabAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
// @public
@@ -357,7 +357,11 @@ export const oktaAuthApiRef: ApiRef<
* for a full list of supported scopes.
*/
export const gitlabAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
> = createApiRef({
id: 'core.auth.gitlab',
});
+5 -1
View File
@@ -23,7 +23,11 @@ export class ScmAuth implements ScmAuthApi {
ScmAuthApi,
{
github: OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi;
gitlab: OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi;
gitlab: OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi;
azure: OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
+4
View File
@@ -23,6 +23,7 @@ import {
createRouteRef,
discoveryApiRef,
identityApiRef,
gitlabAuthApiRef,
googleAuthApiRef,
microsoftAuthApiRef,
oktaAuthApiRef,
@@ -49,18 +50,21 @@ export const kubernetesPlugin = createPlugin({
createApiFactory({
api: kubernetesAuthProvidersApiRef,
deps: {
gitlabAuthApi: gitlabAuthApiRef,
googleAuthApi: googleAuthApiRef,
microsoftAuthApi: microsoftAuthApiRef,
oktaAuthApi: oktaAuthApiRef,
oneloginAuthApi: oneloginAuthApiRef,
},
factory: ({
gitlabAuthApi,
googleAuthApi,
microsoftAuthApi,
oktaAuthApi,
oneloginAuthApi,
}) => {
const oidcProviders = {
gitlab: gitlabAuthApi,
google: googleAuthApi,
microsoft: microsoftAuthApi,
okta: oktaAuthApi,