feat: map preferred_username claim to user record (#2569)

This commit is contained in:
Greg Linklater
2021-09-17 03:45:37 +02:00
committed by GitHub
parent 27736f66ef
commit 01cea549a5
7 changed files with 29 additions and 1 deletions

View File

@ -89,6 +89,10 @@ OIDC_AUTH_URI=
OIDC_TOKEN_URI= OIDC_TOKEN_URI=
OIDC_USERINFO_URI= OIDC_USERINFO_URI=
# Specify which claims to derive user information from
# Supports any valid JSON path with the JWT payload
OIDC_USERNAME_CLAIM=preferred_username
# Display name for OIDC authentication # Display name for OIDC authentication
OIDC_DISPLAY_NAME=OpenID Connect OIDC_DISPLAY_NAME=OpenID Connect

View File

@ -17,6 +17,7 @@ type Props = {|
name: string, name: string,
email: string, email: string,
avatarUrl?: string, avatarUrl?: string,
username?: string,
|}, |},
team: {| team: {|
name: string, name: string,
@ -74,6 +75,7 @@ export default async function accountProvisioner({
const result = await userCreator({ const result = await userCreator({
name: userParams.name, name: userParams.name,
email: userParams.email, email: userParams.email,
username: userParams.username,
isAdmin: isNewTeam, isAdmin: isNewTeam,
avatarUrl: userParams.avatarUrl, avatarUrl: userParams.avatarUrl,
teamId: team.id, teamId: team.id,

View File

@ -32,6 +32,7 @@ describe("accountProvisioner", () => {
name: "Jenny Tester", name: "Jenny Tester",
email: "jenny@example.com", email: "jenny@example.com",
avatarUrl: "https://example.com/avatar.png", avatarUrl: "https://example.com/avatar.png",
username: "jtester",
}, },
team: { team: {
name: "New team", name: "New team",
@ -57,6 +58,7 @@ describe("accountProvisioner", () => {
expect(auth.scopes[0]).toEqual("read"); expect(auth.scopes[0]).toEqual("read");
expect(team.name).toEqual("New team"); expect(team.name).toEqual("New team");
expect(user.email).toEqual("jenny@example.com"); expect(user.email).toEqual("jenny@example.com");
expect(user.username).toEqual("jtester");
expect(isNewUser).toEqual(true); expect(isNewUser).toEqual(true);
expect(isNewTeam).toEqual(true); expect(isNewTeam).toEqual(true);
expect(mailer.sendTemplate).toHaveBeenCalled(); expect(mailer.sendTemplate).toHaveBeenCalled();
@ -73,6 +75,7 @@ describe("accountProvisioner", () => {
const authentications = await existing.getAuthentications(); const authentications = await existing.getAuthentications();
const authentication = authentications[0]; const authentication = authentications[0];
const newEmail = "test@example.com"; const newEmail = "test@example.com";
const newUsername = "tname";
const { user, isNewUser, isNewTeam } = await accountProvisioner({ const { user, isNewUser, isNewTeam } = await accountProvisioner({
ip, ip,
@ -80,6 +83,7 @@ describe("accountProvisioner", () => {
name: existing.name, name: existing.name,
email: newEmail, email: newEmail,
avatarUrl: existing.avatarUrl, avatarUrl: existing.avatarUrl,
username: newUsername,
}, },
team: { team: {
name: existingTeam.name, name: existingTeam.name,
@ -102,6 +106,7 @@ describe("accountProvisioner", () => {
expect(auth.scopes.length).toEqual(1); expect(auth.scopes.length).toEqual(1);
expect(auth.scopes[0]).toEqual("read"); expect(auth.scopes[0]).toEqual("read");
expect(user.email).toEqual(newEmail); expect(user.email).toEqual(newEmail);
expect(user.username).toEqual(newUsername);
expect(isNewTeam).toEqual(false); expect(isNewTeam).toEqual(false);
expect(isNewUser).toEqual(false); expect(isNewUser).toEqual(false);
expect(mailer.sendTemplate).not.toHaveBeenCalled(); expect(mailer.sendTemplate).not.toHaveBeenCalled();
@ -162,6 +167,7 @@ describe("accountProvisioner", () => {
name: "Jenny Tester", name: "Jenny Tester",
email: "jenny@example.com", email: "jenny@example.com",
avatarUrl: "https://example.com/avatar.png", avatarUrl: "https://example.com/avatar.png",
username: "jtester",
}, },
team: { team: {
name: team.name, name: team.name,
@ -186,6 +192,7 @@ describe("accountProvisioner", () => {
expect(auth.scopes.length).toEqual(1); expect(auth.scopes.length).toEqual(1);
expect(auth.scopes[0]).toEqual("read"); expect(auth.scopes[0]).toEqual("read");
expect(user.email).toEqual("jenny@example.com"); expect(user.email).toEqual("jenny@example.com");
expect(user.username).toEqual("jtester");
expect(isNewUser).toEqual(true); expect(isNewUser).toEqual(true);
expect(mailer.sendTemplate).toHaveBeenCalled(); expect(mailer.sendTemplate).toHaveBeenCalled();

View File

@ -14,6 +14,7 @@ type UserCreatorResult = {|
export default async function userCreator({ export default async function userCreator({
name, name,
email, email,
username,
isAdmin, isAdmin,
avatarUrl, avatarUrl,
teamId, teamId,
@ -22,6 +23,7 @@ export default async function userCreator({
}: {| }: {|
name: string, name: string,
email: string, email: string,
username?: string,
isAdmin?: boolean, isAdmin?: boolean,
avatarUrl?: string, avatarUrl?: string,
teamId: string, teamId: string,
@ -63,7 +65,7 @@ export default async function userCreator({
} }
if (user) { if (user) {
await user.update({ email }); await user.update({ email, username });
await auth.update(rest); await auth.update(rest);
return { user, authentication: auth, isNewUser: false }; return { user, authentication: auth, isNewUser: false };
@ -128,6 +130,7 @@ export default async function userCreator({
{ {
name, name,
email, email,
username,
isAdmin, isAdmin,
teamId, teamId,
avatarUrl, avatarUrl,

View File

@ -13,10 +13,12 @@ describe("userCreator", () => {
const authentications = await existing.getAuthentications(); const authentications = await existing.getAuthentications();
const existingAuth = authentications[0]; const existingAuth = authentications[0];
const newEmail = "test@example.com"; const newEmail = "test@example.com";
const newUsername = "tname";
const result = await userCreator({ const result = await userCreator({
name: existing.name, name: existing.name,
email: newEmail, email: newEmail,
username: newUsername,
avatarUrl: existing.avatarUrl, avatarUrl: existing.avatarUrl,
teamId: existing.teamId, teamId: existing.teamId,
ip, ip,
@ -34,6 +36,7 @@ describe("userCreator", () => {
expect(authentication.scopes.length).toEqual(1); expect(authentication.scopes.length).toEqual(1);
expect(authentication.scopes[0]).toEqual("read"); expect(authentication.scopes[0]).toEqual("read");
expect(user.email).toEqual(newEmail); expect(user.email).toEqual(newEmail);
expect(user.username).toEqual(newUsername);
expect(isNewUser).toEqual(false); expect(isNewUser).toEqual(false);
}); });
@ -101,6 +104,7 @@ describe("userCreator", () => {
const result = await userCreator({ const result = await userCreator({
name: "Test Name", name: "Test Name",
email: "test@example.com", email: "test@example.com",
username: "tname",
teamId: team.id, teamId: team.id,
ip, ip,
authentication: { authentication: {
@ -117,6 +121,7 @@ describe("userCreator", () => {
expect(authentication.scopes.length).toEqual(1); expect(authentication.scopes.length).toEqual(1);
expect(authentication.scopes[0]).toEqual("read"); expect(authentication.scopes[0]).toEqual("read");
expect(user.email).toEqual("test@example.com"); expect(user.email).toEqual("test@example.com");
expect(user.username).toEqual("tname");
expect(isNewUser).toEqual(true); expect(isNewUser).toEqual(true);
}); });

View File

@ -2,6 +2,7 @@
import passport from "@outlinewiki/koa-passport"; import passport from "@outlinewiki/koa-passport";
import fetch from "fetch-with-proxy"; import fetch from "fetch-with-proxy";
import Router from "koa-router"; import Router from "koa-router";
import get from "lodash/get";
import { Strategy } from "passport-oauth2"; import { Strategy } from "passport-oauth2";
import accountProvisioner from "../../../commands/accountProvisioner"; import accountProvisioner from "../../../commands/accountProvisioner";
import env from "../../../env"; import env from "../../../env";
@ -22,6 +23,8 @@ const OIDC_AUTH_URI = process.env.OIDC_AUTH_URI;
const OIDC_TOKEN_URI = process.env.OIDC_TOKEN_URI; const OIDC_TOKEN_URI = process.env.OIDC_TOKEN_URI;
const OIDC_USERINFO_URI = process.env.OIDC_USERINFO_URI; const OIDC_USERINFO_URI = process.env.OIDC_USERINFO_URI;
const OIDC_SCOPES = process.env.OIDC_SCOPES || ""; const OIDC_SCOPES = process.env.OIDC_SCOPES || "";
const OIDC_USERNAME_CLAIM =
process.env.OIDC_USERNAME_CLAIM || "preferred_username";
const allowedDomains = getAllowedDomains(); const allowedDomains = getAllowedDomains();
export const config = { export const config = {
@ -103,6 +106,9 @@ if (OIDC_CLIENT_ID) {
name: profile.name, name: profile.name,
email: profile.email, email: profile.email,
avatarUrl: profile.picture, avatarUrl: profile.picture,
// Claim name can be overriden using an env variable.
// Default is 'preferred_username' as per OIDC spec.
username: get(profile, OIDC_USERNAME_CLAIM),
}, },
authenticationProvider: { authenticationProvider: {
name: providerName, name: providerName,

View File

@ -104,6 +104,7 @@ export async function buildUser(overrides: Object = {}) {
{ {
email: `user${count}@example.com`, email: `user${count}@example.com`,
name: `User ${count}`, name: `User ${count}`,
username: `user${count}`,
createdAt: new Date("2018-01-01T00:00:00.000Z"), createdAt: new Date("2018-01-01T00:00:00.000Z"),
lastActiveAt: new Date("2018-01-01T00:00:00.000Z"), lastActiveAt: new Date("2018-01-01T00:00:00.000Z"),
authentications: [ authentications: [