fix casing of sub and ent claims in default sign-in resolvers
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend': minor
|
||||
---
|
||||
|
||||
**BREAKING**: The default sign-in resolvers for all providers, if you choose to
|
||||
use them, now emit the token `sub` and `ent` claims on the standard,
|
||||
all-lowercase form, instead of the mixed-case form. The mixed-case form causes
|
||||
problems for implementations that naively do string comparisons on refs. The end
|
||||
result is that you may for example see your Backstage token `sub` claim now
|
||||
become `'user:default/my-id'` instead of `'user:default/My-ID'`.
|
||||
|
||||
On a related note, specifically the SAML provider now correctly issues both
|
||||
`sub` and `ent` claims, and on the full entity ref form instead of the short
|
||||
form with only the ID.
|
||||
|
||||
**NOTE**: For a long time, it has been strongly recommended that you provide
|
||||
your own sign-in resolver instead of using the builtin ones, and that will
|
||||
become mandatory in the future.
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
import { Logger } from 'winston';
|
||||
import { Profile as PassportProfile } from 'passport';
|
||||
@@ -247,10 +251,16 @@ export const githubDefaultSignInResolver: SignInResolver<
|
||||
|
||||
const userId = fullProfile.username || fullProfile.id;
|
||||
|
||||
const entityRef = stringifyEntityRef({
|
||||
kind: 'User',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: userId,
|
||||
});
|
||||
|
||||
const token = await ctx.tokenIssuer.issueToken({
|
||||
claims: {
|
||||
sub: `user:default/${userId}`,
|
||||
ent: [`user:default/${userId}`],
|
||||
sub: entityRef,
|
||||
ent: [entityRef],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -14,10 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
import { Strategy as GitlabStrategy } from 'passport-gitlab2';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
import {
|
||||
executeRedirectStrategy,
|
||||
executeFrameHandlerStrategy,
|
||||
@@ -71,8 +74,17 @@ export const gitlabDefaultSignInResolver: SignInResolver<OAuthResult> = async (
|
||||
id = profile.email.split('@')[0];
|
||||
}
|
||||
|
||||
const entityRef = stringifyEntityRef({
|
||||
kind: 'User',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: id,
|
||||
});
|
||||
|
||||
const token = await ctx.tokenIssuer.issueToken({
|
||||
claims: { sub: `user:default/${id}`, ent: [`user:default/${id}`] },
|
||||
claims: {
|
||||
sub: entityRef,
|
||||
ent: [entityRef],
|
||||
},
|
||||
});
|
||||
|
||||
return { id, token };
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
import passport from 'passport';
|
||||
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
|
||||
@@ -226,8 +230,17 @@ const googleDefaultSignInResolver: SignInResolver<OAuthResult> = async (
|
||||
userId = profile.email.split('@')[0];
|
||||
}
|
||||
|
||||
const entityRef = stringifyEntityRef({
|
||||
kind: 'User',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: userId,
|
||||
});
|
||||
|
||||
const token = await ctx.tokenIssuer.issueToken({
|
||||
claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] },
|
||||
claims: {
|
||||
sub: entityRef,
|
||||
ent: [entityRef],
|
||||
},
|
||||
});
|
||||
|
||||
return { id: userId, token };
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
import passport from 'passport';
|
||||
import { Strategy as MicrosoftStrategy } from 'passport-microsoft';
|
||||
@@ -231,10 +235,16 @@ export const microsoftDefaultSignInResolver: SignInResolver<
|
||||
|
||||
const userId = profile.email.split('@')[0];
|
||||
|
||||
const entityRef = stringifyEntityRef({
|
||||
kind: 'User',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: userId,
|
||||
});
|
||||
|
||||
const token = await ctx.tokenIssuer.issueToken({
|
||||
claims: {
|
||||
sub: `user:default/${userId}`,
|
||||
ent: [`user:default/${userId}`],
|
||||
sub: entityRef,
|
||||
ent: [entityRef],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -15,5 +15,4 @@
|
||||
*/
|
||||
|
||||
export { createOAuth2Provider } from './provider';
|
||||
|
||||
export type { OAuth2ProviderOptions } from './provider';
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
import passport from 'passport';
|
||||
import { Strategy as OAuth2Strategy } from 'passport-oauth2';
|
||||
@@ -210,8 +214,17 @@ export const oAuth2DefaultSignInResolver: SignInResolver<OAuthResult> = async (
|
||||
|
||||
const userId = profile.email.split('@')[0];
|
||||
|
||||
const entityRef = stringifyEntityRef({
|
||||
kind: 'User',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: userId,
|
||||
});
|
||||
|
||||
const token = await ctx.tokenIssuer.issueToken({
|
||||
claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] },
|
||||
claims: {
|
||||
sub: entityRef,
|
||||
ent: [entityRef],
|
||||
},
|
||||
});
|
||||
|
||||
return { id: userId, token };
|
||||
|
||||
@@ -13,5 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type { OidcAuthResult, OidcProviderOptions } from './provider';
|
||||
export { createOidcProvider } from './provider';
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
import {
|
||||
Client,
|
||||
@@ -211,21 +215,31 @@ export class OidcAuthProvider implements OAuthHandlers {
|
||||
}
|
||||
}
|
||||
|
||||
export const oAuth2DefaultSignInResolver: SignInResolver<
|
||||
OidcAuthResult
|
||||
> = async (info, ctx) => {
|
||||
export const oidcDefaultSignInResolver: SignInResolver<OidcAuthResult> = async (
|
||||
info,
|
||||
ctx,
|
||||
) => {
|
||||
const { profile } = info;
|
||||
|
||||
if (!profile.email) {
|
||||
throw new Error('Profile contained no email');
|
||||
}
|
||||
|
||||
const userId = profile.email.split('@')[0];
|
||||
|
||||
const entityRef = stringifyEntityRef({
|
||||
kind: 'User',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: userId,
|
||||
});
|
||||
|
||||
const token = await ctx.tokenIssuer.issueToken({
|
||||
claims: {
|
||||
sub: `user:default/${userId}`,
|
||||
ent: [`user:default/${userId}`],
|
||||
sub: entityRef,
|
||||
ent: [entityRef],
|
||||
},
|
||||
});
|
||||
|
||||
return { id: userId, token };
|
||||
};
|
||||
|
||||
@@ -289,7 +303,7 @@ export const createOidcProvider = (
|
||||
},
|
||||
});
|
||||
const signInResolverFn =
|
||||
options?.signIn?.resolver ?? oAuth2DefaultSignInResolver;
|
||||
options?.signIn?.resolver ?? oidcDefaultSignInResolver;
|
||||
const signInResolver: SignInResolver<OidcAuthResult> = info =>
|
||||
signInResolverFn(info, {
|
||||
catalogIdentityClient,
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
import {
|
||||
OAuthAdapter,
|
||||
@@ -235,8 +240,17 @@ export const oktaDefaultSignInResolver: SignInResolver<OAuthResult> = async (
|
||||
// TODO(Rugvip): Hardcoded to the local part of the email for now
|
||||
const userId = profile.email.split('@')[0];
|
||||
|
||||
const entityRef = stringifyEntityRef({
|
||||
kind: 'User',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: userId,
|
||||
});
|
||||
|
||||
const token = await ctx.tokenIssuer.issueToken({
|
||||
claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] },
|
||||
claims: {
|
||||
sub: entityRef,
|
||||
ent: [entityRef],
|
||||
},
|
||||
});
|
||||
|
||||
return { id: userId, token };
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
import { SamlConfig } from 'passport-saml/lib/passport-saml/types';
|
||||
import {
|
||||
@@ -150,8 +154,17 @@ const samlDefaultSignInResolver: SignInResolver<SamlAuthResult> = async (
|
||||
) => {
|
||||
const id = info.result.fullProfile.nameID;
|
||||
|
||||
const entityRef = stringifyEntityRef({
|
||||
kind: 'User',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: id,
|
||||
});
|
||||
|
||||
const token = await ctx.tokenIssuer.issueToken({
|
||||
claims: { sub: id },
|
||||
claims: {
|
||||
sub: entityRef,
|
||||
ent: [entityRef],
|
||||
},
|
||||
});
|
||||
|
||||
return { id, token };
|
||||
|
||||
Reference in New Issue
Block a user