docs: add sign-in resolvers in getting started auth

Signed-off-by: Himanshu Mishra <himanshu@orkohunter.net>
This commit is contained in:
Himanshu Mishra
2022-06-08 13:11:30 +02:00
parent f45ce9c814
commit d09cee28ba
+90 -61
View File
@@ -79,6 +79,7 @@ Now let's look at the example, with the rest of the commentary being made with i
the code comments:
```ts
// File: packages/backend/src/plugins/auth.ts
import {
createRouter,
providers,
@@ -139,11 +140,21 @@ property of each of the auth provider integrations. For example, the Google prov
a built in resolver that works just like the one we defined above:
```ts
providers.google.create({
signIn: {
resolver: providers.google.resolvers.emailLocalPartMatchingUserEntityName(),
},
});
// File: packages/backend/src/plugins/auth.ts
export default async function createPlugin(
// ...
return await createRouter({
// ...
providerFactories: {
// ...
google: providers.google.create({
signIn: {
resolver: providers.google.resolvers.emailLocalPartMatchingUserEntityName(),
},
});
}
})
)
```
There are also other options, like the this one that looks up a user
@@ -164,40 +175,49 @@ that happens during sign-in you can replace `ctx.signInWithCatalogUser` with a s
of lower-level calls:
```ts
// File: packages/backend/src/plugins/auth.ts
import { getDefaultOwnershipRefs } from '@backstage/plugin-auth-backend';
// This example only shows the resolver function itself.
async ({ profile: { email } }, ctx) => {
if (!email) {
throw new Error('User profile contained no email');
}
export default async function createPlugin(
// ...
return await createRouter({
// ...
providerFactories: {
// ...
google: async ({ profile: { email } }, ctx) => {
if (!email) {
throw new Error('User profile contained no email');
}
// This step calls the catalog to look up a user entity. You could for example
// replace it with a call to a different external system.
const { entity } = await ctx.findCatalogUser({
annotations: {
'acme.org/email': email,
},
});
// This step calls the catalog to look up a user entity. You could for example
// replace it with a call to a different external system.
const { entity } = await ctx.findCatalogUser({
annotations: {
'acme.org/email': email,
},
});
// In this step we extract the ownership references from the user entity using
// the standard logic. It uses a reference to the entity itself, as well as the
// target of each `memberOf` relation where the target is of the kind `Group`.
//
// If you replace the catalog lookup with something does not return
// an entity you will need to replace this step as well.
//
// You might also replace it if you for example want to filter out certain groups.
const ownershipRefs = getDefaultOwnershipRefs(entity);
// In this step we extract the ownership references from the user entity using
// the standard logic. It uses a reference to the entity itself, as well as the
// target of each `memberOf` relation where the target is of the kind `Group`.
//
// If you replace the catalog lookup with something does not return
// an entity you will need to replace this step as well.
//
// You might also replace it if you for example want to filter out certain groups.
const ownershipRefs = getDefaultOwnershipRefs(entity);
// The last step is to issue the token, where we might provide more options in the future.
return ctx.issueToken({
claims: {
sub: stringifyEntityRef(entity),
ent: ownershipRefs,
},
});
};
// The last step is to issue the token, where we might provide more options in the future.
return ctx.issueToken({
claims: {
sub: stringifyEntityRef(entity),
ent: ownershipRefs,
},
});
};
}
})
)
```
## Sign-In without Users in the Catalog
@@ -221,38 +241,47 @@ check like in the example below, or you might for example look up the GitHub org
that the user belongs to using the user access token in the provided result object.
```ts
// File: packages/backend/src/plugins/auth.ts
import { DEFAULT_NAMESPACE, stringifyEntityRef, } from '@backstage/catalog-model';
// This example only shows the resolver function itself.
async ({ profile }, ctx) => {
if (!profile.email) {
throw new Error(
'Login failed, user profile does not contain an email',
);
}
// Split the email into the local part and the domain.
const [localPart, domain] = profile.email.split('@');
export default async function createPlugin(
// ...
return await createRouter({
// ...
providerFactories: {
// ...
google: async ({ profile }, ctx) => {
if (!profile.email) {
throw new Error(
'Login failed, user profile does not contain an email',
);
}
// Split the email into the local part and the domain.
const [localPart, domain] = profile.email.split('@');
// Next we verify the email domain. It is recommended to include this
// kind of check if you don't look up the user in an external service.
if (domain !== 'acme.org') {
throw new Error('Login failed, user email domain check failed');
}
// Next we verify the email domain. It is recommended to include this
// kind of check if you don't look up the user in an external service.
if (domain !== 'acme.org') {
throw new Error('Login failed, user email domain check failed');
}
// By using `stringifyEntityRef` we ensure that the reference is formatted correctly
const userEntityRef = stringifyEntityRef({
kind: 'User',
name: localPart,
namespace: DEFAULT_NAMESPACE,
});
// By using `stringifyEntityRef` we ensure that the reference is formatted correctly
const userEntityRef = stringifyEntityRef({
kind: 'User',
name: localPart,
namespace: DEFAULT_NAMESPACE,
});
return ctx.issueToken({
claims: {
sub: userEntityRef,
ent: [userEntityRef],
},
});
},
return ctx.issueToken({
claims: {
sub: userEntityRef,
ent: [userEntityRef],
},
});
},
}
})
)
```
## AuthHandler