fix microsoft auth and ingestion for users without defined email

Signed-off-by: Jessica He <jhe@redhat.com>
This commit is contained in:
Jessica He
2024-08-22 15:21:31 -04:00
parent f54d3a5385
commit 3c2d690ce2
7 changed files with 77 additions and 5 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-auth-backend-module-microsoft-provider': minor
'@backstage/plugin-catalog-backend-module-msgraph': patch
---
Allow users without defined email to be ingested by the `msgraph` catalog plugin and add `userIdMatchingUserEntityAnnotation` sign-in resolver for the Microsoft auth provider to support sign-in for users without defined email.
@@ -32,5 +32,9 @@ export namespace microsoftSignInResolvers {
OAuthAuthenticatorResult<PassportProfile>,
unknown
>;
const userIdMatchingUserEntityAnnotation: SignInResolverFactory<
OAuthAuthenticatorResult<PassportProfile>,
unknown
>;
}
```
@@ -28,7 +28,7 @@ import {
*/
export namespace microsoftSignInResolvers {
/**
* Looks up the user by matching their Microsoft username to the entity name.
* Looks up the user by matching their Microsoft email to the email entity annotation.
*/
export const emailMatchingUserEntityAnnotation = createSignInResolverFactory({
create() {
@@ -50,4 +50,31 @@ export namespace microsoftSignInResolvers {
};
},
});
/**
* Looks up the user by matching their Microsoft user id to the user id entity annotation.
*/
export const userIdMatchingUserEntityAnnotation = createSignInResolverFactory(
{
create() {
return async (
info: SignInInfo<OAuthAuthenticatorResult<PassportProfile>>,
ctx,
) => {
const { result } = info;
const id = result.fullProfile.id;
if (!id) {
throw new Error('Microsoft profile contained no id');
}
return ctx.signInWithCatalogUser({
annotations: {
'graph.microsoft.com/user-id': id,
},
});
};
},
},
);
}
+1
View File
@@ -533,6 +533,7 @@ export const providers: Readonly<{
resolvers: Readonly<{
emailMatchingUserEntityProfileEmail: () => SignInResolver_2<OAuthResult>;
emailLocalPartMatchingUserEntityName: () => SignInResolver_2<OAuthResult>;
userIdMatchingUserEntityAnnotation: () => SignInResolver_2<OAuthResult>;
emailMatchingUserEntityAnnotation: () => SignInResolver_2<OAuthResult>;
}>;
}>;
@@ -65,5 +65,7 @@ export const microsoft = createAuthProviderIntegration({
commonSignInResolvers.emailMatchingUserEntityProfileEmail(),
emailMatchingUserEntityAnnotation:
microsoftSignInResolvers.emailMatchingUserEntityAnnotation(),
userIdMatchingUserEntityAnnotation:
microsoftSignInResolvers.userIdMatchingUserEntityAnnotation(),
}),
});
@@ -90,4 +90,31 @@ describe('defaultTransformers', () => {
},
});
});
it('tests defaultUserTransformer with no email', async () => {
const user: MicrosoftGraph.User = {
id: 'foo',
displayName: 'BAR',
userPrincipalName: 'test@upn',
};
const userPhoto = 'test_photo';
const result = await defaultUserTransformer(user, userPhoto);
expect(result).toEqual({
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
annotations: {
'graph.microsoft.com/user-id': 'foo',
},
name: 'test_upn',
},
spec: {
memberOf: [],
profile: {
displayName: 'BAR',
picture: 'test_photo',
},
},
});
});
});
@@ -122,25 +122,25 @@ export async function defaultUserTransformer(
user: MicrosoftGraph.User,
userPhoto?: string,
): Promise<UserEntity | undefined> {
if (!user.id || !user.displayName || !user.mail) {
if (!user.id || !user.displayName) {
return undefined;
}
const name = normalizeEntityName(user.mail);
const name = user.mail
? normalizeEntityName(user.mail)
: normalizeEntityName(user.userPrincipalName!);
const entity: UserEntity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
name,
annotations: {
[MICROSOFT_EMAIL_ANNOTATION]: user.mail!,
[MICROSOFT_GRAPH_USER_ID_ANNOTATION]: user.id!,
},
},
spec: {
profile: {
displayName: user.displayName!,
email: user.mail!,
// TODO: Additional fields?
// jobTitle: user.jobTitle || undefined,
@@ -151,6 +151,11 @@ export async function defaultUserTransformer(
},
};
if (user.mail) {
entity.metadata.annotations![MICROSOFT_EMAIL_ANNOTATION] = user.mail;
entity.spec.profile!.email = user.mail;
}
if (userPhoto) {
entity.spec.profile!.picture = userPhoto;
}