Skip to content

Commit

Permalink
WIP[api] add microsoft strategy to auth module
Browse files Browse the repository at this point in the history
  • Loading branch information
freemvmt committed Jul 11, 2024
1 parent b8da414 commit f1f245d
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 0 deletions.
13 changes: 13 additions & 0 deletions api.planx.uk/modules/auth/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,19 @@ export const useGoogleCallbackAuth: RequestHandler = (req, res, next) => {
})(req, res, next);
};

export const useMicrosoftAuth: RequestHandler = (req, res, next) => {
req.session!.returnTo = req.get("Referrer");
return passport.authenticate("microsoft-oidc", {
prompt: "select_account",
})(req, res, next);
};

export const useMicrosoftCallbackAuth: RequestHandler = (req, res, next) => {
return passport.authenticate("microsoft-oidc", {
failureRedirect: "/auth/login/failed",
})(req, res, next);
};

type UseRoleAuth = (authRoles: Role[]) => RequestHandler;

/**
Expand Down
6 changes: 6 additions & 0 deletions api.planx.uk/modules/auth/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@ router.get(
Middleware.useGoogleCallbackAuth,
Controller.handleSuccess,
);
router.get("/auth/microsoft", Middleware.useMicrosoftAuth)
router.get(
"/auth/microsoft/callback",
Middleware.useMicrosoftCallbackAuth,
Controller.handleSuccess,
);

export default router;
72 changes: 72 additions & 0 deletions api.planx.uk/modules/auth/strategy/microsoft-oidc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Strategy, TokenSet, Issuer, generators, Client } from "openid-client";
import { buildJWT } from "../service";

const MICROSOFT_OAUTH_BASE_URL =
"https://login.microsoftonline.com/common/v2.0";
const OPENID_METADATA_DOCUMENT_ENDPOINT = "/.well-known/openid-configuration";

export const getMicrosoftIssuer = async (): Promise<Issuer> => {
const microsoftIssuer = await Issuer.discover(
MICROSOFT_OAUTH_BASE_URL + OPENID_METADATA_DOCUMENT_ENDPOINT,
);
console.log(
"Discovered issuer %s %O",
microsoftIssuer.issuer,
microsoftIssuer.metadata,
);
return microsoftIssuer;
};

export const getMicrosoftOidcStrategy = (
microsoftIssuer: Issuer,
): Strategy<Client> => {
console.log("redirect uri domain:");
console.log(process.env.API_URL_EXT);

const microsoftClient = new microsoftIssuer.Client({
client_id: process.env.MICROSOFT_CLIENT_ID!,
client_secret: process.env.MICROSOFT_CLIENT_SECRET!,
redirect_uris: [`${process.env.API_URL_EXT}/auth/microsoft/callback`],
post_logout_redirect_uris: [`${process.env.API_URL_EXT}/logout`],
response_types: ["id_token"],
});

const nonce = generators.nonce();
console.log(`Generated a nonce: ${nonce}`);
// TODO: store nonce (encrypted and httpOnly) in session

microsoftClient.authorizationUrl({
scope: "openid email profile",
response_mode: "form_post", // could also be 'query' or 'fragment'
nonce,
});

console.log("Built Microsoft client:");
console.log(microsoftClient.metadata);

// oidc = Open ID Connect
return new Strategy(
{ client: microsoftClient },
async (tokenset: TokenSet, userInfo: any, done: any): Promise<void> => {
console.log("USER INFO:");
console.log(userInfo);

console.log("TOKEN SET:");
console.log(tokenset);

const email = "xxx";
if (!email) throw Error("Unable to authenticate without email");

const jwt = await buildJWT(email);

if (!jwt) {
return done({
status: 404,
message: `User (${email}) not found. Do you need to log in to a different Microsoft Account?`,
} as any);
}

done(null, { jwt });
},
);
};
1 change: 1 addition & 0 deletions api.planx.uk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"multer": "^1.4.5-lts.1",
"nanoid": "^3.3.7",
"notifications-node-client": "^8.2.0",
"openid-client": "^5.6.5",
"passport": "^0.5.3",
"passport-google-oauth20": "^2.0.0",
"pino-noir": "^2.2.1",
Expand Down
37 changes: 37 additions & 0 deletions api.planx.uk/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions api.planx.uk/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import { ServerError } from "./errors";
import airbrake from "./airbrake";
import { apiLimiter } from "./rateLimit";
import { googleStrategy } from "./modules/auth/strategy/google";
import {
getMicrosoftIssuer,
getMicrosoftOidcStrategy,
} from "./modules/auth/strategy/microsoft-oidc";
import { Issuer } from "openid-client";
import authRoutes from "./modules/auth/routes";
import teamRoutes from "./modules/team/routes";
import miscRoutes from "./modules/misc/routes";
Expand Down Expand Up @@ -116,6 +121,11 @@ app.use(
}),
);

// we have to fetch the Microsoft OpenID issuer to pass to our strategy constructor
// TODO: handle failure to fetch issuer
getMicrosoftIssuer().then((microsoftIssuer: Issuer) => {
passport.use("microsoft-oidc", getMicrosoftOidcStrategy(microsoftIssuer));
});
passport.use("google", googleStrategy);

passport.serializeUser(function (user, cb) {
Expand Down
10 changes: 10 additions & 0 deletions editor.planx.uk/src/pages/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ const Login: React.FC = () => {
>
Login with Google
</Link>
<Link
variant="body1"
color="#FFFFFF"
href={`${
process.env.REACT_APP_MICROSOFT_OAUTH_OVERRIDE ??
process.env.REACT_APP_API_URL
}/auth/microsoft`}
>
Login with Microsoft
</Link>
</LoginContainer>
</Wrapper>
);
Expand Down

0 comments on commit f1f245d

Please sign in to comment.