-
Notifications
You must be signed in to change notification settings - Fork 144
Expand file tree
/
Copy pathopenidHelper.ts
More file actions
63 lines (58 loc) · 2.21 KB
/
openidHelper.ts
File metadata and controls
63 lines (58 loc) · 2.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import * as client from "openid-client";
/**
* Normalize an `openIdIssuer` URL so that `openid-client`'s discovery
* (`<issuer>/.well-known/openid-configuration`) resolves in both local CLI
* and deployed SWA environments.
*
* Background: the Microsoft identity platform v2.0 canonical issuer is
* `https://login.microsoftonline.com/<tenant>/v2.0`. A legacy/alias form,
* `https://login.microsoftonline.com/<tenant>/oauth2/v2.0`, is accepted by
* the deployed SWA runtime but causes the CLI's OIDC discovery to fail
* (ERR_TOO_MANY_REDIRECTS or 404). We strip the trailing `/oauth2` segment
* so that users can keep a single `staticwebapp.config.json` that works
* both locally and when deployed.
*
* The same normalization applies to Entra External ID (`*.ciamlogin.com`)
* and Entra custom URL domains — any `.../<tenant>/oauth2/v2.0` suffix is
* rewritten to `.../<tenant>/v2.0`.
*
* See: https://github.com/Azure/static-web-apps-cli/issues/947
*/
export function normalizeOpenIdIssuer(issuer: string): string {
if (!issuer) {
return issuer;
}
// Rewrite `/oauth2/v2.0` (with optional trailing slash) to `/v2.0` while
// preserving any trailing slash the user provided.
return issuer.replace(/\/oauth2\/v2\.0(\/?)$/, "/v2.0$1");
}
export class OpenIdHelper {
private issuerUrl: URL;
private clientId: string;
constructor(issuerUrl: string, clientId: string) {
if (!issuerUrl || issuerUrl.trim() === "") {
throw new Error("Issuer URL is required");
}
if (!clientId || clientId.trim() === "") {
throw new Error("Client ID is required");
}
this.issuerUrl = new URL(normalizeOpenIdIssuer(issuerUrl));
this.clientId = clientId;
}
/**
* Discover issuer metadata from the OpenID Connect provider
*/
async discoverIssuer() {
return await client.discovery(this.issuerUrl, this.clientId);
}
/**
* Retrieve the authorization endpoint from the issuer
*/
async getAuthorizationEndpoint(): Promise<string> {
const issuer = await this.discoverIssuer();
if (!issuer.serverMetadata().authorization_endpoint) {
throw new Error("Authorization endpoint not found in issuer metadata");
}
return issuer.serverMetadata().authorization_endpoint!;
}
}