Skip to content

Commit

Permalink
Revert "Merge pull request Azure#880 from Timothyw0/main"
Browse files Browse the repository at this point in the history
This reverts commit 5f440e3, reversing
changes made to b298821.
  • Loading branch information
Timothy Wang committed Dec 15, 2024
1 parent dea34ff commit ed735e6
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 142 deletions.
30 changes: 8 additions & 22 deletions package-lock.json

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

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
"internal-ip": "^6.2.0",
"json-schema-library": "^9.3.5",
"json-source-map": "^0.6.1",
"jwt-decode": "^4.0.0",
"keytar": "^7.9.0",
"node-fetch": "^2.7.0",
"open": "^8.4.2",
Expand Down
13 changes: 1 addition & 12 deletions src/core/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const SWA_AUTH_COOKIE = `StaticWebAppsAuthCookie`;
export const ALLOWED_HTTP_METHODS_FOR_STATIC_CONTENT = ["GET", "HEAD", "OPTIONS"];

// Custom Auth constants
export const SUPPORTED_CUSTOM_AUTH_PROVIDERS = ["google", "github", "aad", "facebook", "dummy"];
export const SUPPORTED_CUSTOM_AUTH_PROVIDERS = ["google", "github", "aad", "dummy"];
/*
The full name is required in staticwebapp.config.json's schema that will be normalized to aad
https://learn.microsoft.com/en-us/azure/static-web-apps/authentication-custom?tabs=aad%2Cinvitations
Expand All @@ -69,10 +69,6 @@ export const CUSTOM_AUTH_TOKEN_ENDPOINT_MAPPING: AuthIdentityTokenEndpoints = {
host: "login.microsoftonline.com",
path: "/tenantId/oauth2/v2.0/token",
},
facebook: {
host: "graph.facebook.com",
path: "/v11.0/oauth/access_token",
},
};
export const CUSTOM_AUTH_USER_ENDPOINT_MAPPING: AuthIdentityTokenEndpoints = {
google: {
Expand All @@ -92,13 +88,6 @@ export const CUSTOM_AUTH_ISS_MAPPING: AuthIdentityIssHosts = {
google: "https://account.google.com",
github: "",
aad: "https://graph.microsoft.com",
facebook: "https://www.facebook.com",
};
export const CUSTOM_AUTH_REQUIRED_FIELDS: AuthIdentityRequiredFields = {
google: ["clientIdSettingName", "clientSecretSettingName"],
github: ["clientIdSettingName", "clientSecretSettingName"],
aad: ["clientIdSettingName", "clientSecretSettingName", "openIdIssuer"],
facebook: ["appIdSettingName", "appSecretSettingName"],
};

export const AUTH_STATUS = {
Expand Down
2 changes: 1 addition & 1 deletion src/msha/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function getAuthPaths(isCustomAuth: boolean): Path[] {
paths.push({
method: "GET",
// For providers with custom auth support not implemented, revert to old behavior
route: /^\/\.auth\/login\/(?<provider>twitter|[a-z]+)(\?.*)?$/i,
route: /^\/\.auth\/login\/(?<provider>twitter|facebook|[a-z]+)(\?.*)?$/i,
function: "auth-login-provider",
});
paths.push({
Expand Down
153 changes: 100 additions & 53 deletions src/msha/auth/routes/auth-login-provider-callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as querystring from "node:querystring";
import { CookiesManager, decodeAuthContextCookie, validateAuthContextCookie } from "../../../core/utils/cookie.js";
import { parseUrl, response } from "../../../core/utils/net.js";
import {
ENTRAID_FULL_NAME,
CUSTOM_AUTH_ISS_MAPPING,
CUSTOM_AUTH_TOKEN_ENDPOINT_MAPPING,
CUSTOM_AUTH_USER_ENDPOINT_MAPPING,
Expand All @@ -14,27 +15,26 @@ import {
} from "../../../core/constants.js";
import { DEFAULT_CONFIG } from "../../../config.js";
import { encryptAndSign, hashStateGuid, isNonceExpired } from "../../../core/utils/auth.js";
import { checkCustomAuthConfigFields, normalizeAuthProvider } from "./auth-login-provider-custom.js";
import { jwtDecode } from "jwt-decode";

const getAuthClientPrincipal = async function (authProvider: string, codeValue: string, authConfigs: Record<string, string>) {
import { normalizeAuthProvider } from "./auth-login-provider-custom.js";

const getAuthClientPrincipal = async function (
authProvider: string,
codeValue: string,
clientId: string,
clientSecret: string,
openIdIssuer: string = "",
) {
let authToken: string;

try {
const authTokenResponse = (await getOAuthToken(authProvider, codeValue!, authConfigs)) as string;
const authTokenResponse = (await getOAuthToken(authProvider, codeValue!, clientId, clientSecret, openIdIssuer)) as string;
let authTokenParsed;
try {
authTokenParsed = JSON.parse(authTokenResponse);
} catch (e) {
authTokenParsed = querystring.parse(authTokenResponse);
}

// Facebook sends back a JWT in the id_token
if (authProvider !== "facebook") {
authToken = authTokenParsed["access_token"] as string;
} else {
authToken = authTokenParsed["id_token"] as string;
}
authToken = authTokenParsed["access_token"] as string;
} catch (error) {
console.error(`Error in getting OAuth token: ${error}`);
return null;
Expand Down Expand Up @@ -62,11 +62,11 @@ const getAuthClientPrincipal = async function (authProvider: string, codeValue:
},
{
typ: "azp",
val: authConfigs?.clientIdSettingName || authConfigs?.appIdSettingName,
val: clientId,
},
{
typ: "aud",
val: authConfigs?.clientIdSettingName || authConfigs?.appIdSettingName,
val: clientId,
},
];

Expand Down Expand Up @@ -139,7 +139,7 @@ const getAuthClientPrincipal = async function (authProvider: string, codeValue:
}
};

const getOAuthToken = function (authProvider: string, codeValue: string, authConfigs: Record<string, string>) {
const getOAuthToken = function (authProvider: string, codeValue: string, clientId: string, clientSecret: string, openIdIssuer: string = "") {
const redirectUri = `${SWA_CLI_APP_PROTOCOL}://${DEFAULT_CONFIG.host}:${DEFAULT_CONFIG.port}`;
let tenantId;

Expand All @@ -148,13 +148,13 @@ const getOAuthToken = function (authProvider: string, codeValue: string, authCon
}

if (authProvider === "aad") {
tenantId = authConfigs?.openIdIssuer.split("/")[3];
tenantId = openIdIssuer.split("/")[3];
}

const data = querystring.stringify({
code: codeValue,
client_id: authConfigs?.clientIdSettingName || authConfigs?.appIdSettingName,
client_secret: authConfigs?.clientSecretSettingName || authConfigs?.appSecretSettingName,
client_id: clientId,
client_secret: clientSecret,
grant_type: "authorization_code",
redirect_uri: `${redirectUri}/.auth/login/${authProvider}/callback`,
});
Expand Down Expand Up @@ -198,45 +198,40 @@ const getOAuthToken = function (authProvider: string, codeValue: string, authCon
};

const getOAuthUser = function (authProvider: string, accessToken: string) {
// Facebook does not have an OIDC introspection so we need to manually decode the token :(
if (authProvider === "facebook") {
return jwtDecode(accessToken);
} else {
const options = {
host: CUSTOM_AUTH_USER_ENDPOINT_MAPPING?.[authProvider]?.host,
path: CUSTOM_AUTH_USER_ENDPOINT_MAPPING?.[authProvider]?.path,
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
"User-Agent": "Azure Static Web Apps Emulator",
},
};

return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
res.setEncoding("utf8");
let responseBody = "";
const options = {
host: CUSTOM_AUTH_USER_ENDPOINT_MAPPING?.[authProvider]?.host,
path: CUSTOM_AUTH_USER_ENDPOINT_MAPPING?.[authProvider]?.path,
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
"User-Agent": "Azure Static Web Apps Emulator",
},
};

res.on("data", (chunk) => {
responseBody += chunk;
});
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
res.setEncoding("utf8");
let responseBody = "";

res.on("end", () => {
try {
resolve(JSON.parse(responseBody));
} catch (err) {
reject(err);
}
});
res.on("data", (chunk) => {
responseBody += chunk;
});

req.on("error", (err) => {
reject(err);
res.on("end", () => {
try {
resolve(JSON.parse(responseBody));
} catch (err) {
reject(err);
}
});
});

req.end();
req.on("error", (err) => {
reject(err);
});
}

req.end();
});
};

const getRoles = function (clientPrincipal: RolesSourceFunctionRequestBody, rolesSource: string) {
Expand Down Expand Up @@ -339,12 +334,64 @@ const httpTrigger = async function (context: Context, request: http.IncomingMess
return;
}

const authConfigs = checkCustomAuthConfigFields(context, providerName, customAuth);
if (!authConfigs) {
const { clientIdSettingName, clientSecretSettingName, openIdIssuer } =
customAuth?.identityProviders?.[providerName == "aad" ? ENTRAID_FULL_NAME : providerName]?.registration || {};

if (!clientIdSettingName) {
context.res = response({
context,
status: 400,
headers: { ["Content-Type"]: "text/plain" },
body: `ClientIdSettingName not found for '${providerName}' provider`,
});
return;
}

if (!clientSecretSettingName) {
context.res = response({
context,
status: 400,
headers: { ["Content-Type"]: "text/plain" },
body: `ClientSecretSettingName not found for '${providerName}' provider`,
});
return;
}

if (providerName == "aad" && !openIdIssuer) {
context.res = response({
context,
status: 400,
headers: { ["Content-Type"]: "text/plain" },
body: `openIdIssuer not found for '${providerName}' provider`,
});
return;
}

const clientId = process.env[clientIdSettingName];

if (!clientId) {
context.res = response({
context,
status: 400,
headers: { ["Content-Type"]: "text/plain" },
body: `ClientId not found for '${providerName}' provider`,
});
return;
}

const clientSecret = process.env[clientSecretSettingName];

if (!clientSecret) {
context.res = response({
context,
status: 400,
headers: { ["Content-Type"]: "text/plain" },
body: `ClientSecret not found for '${providerName}' provider`,
});
return;
}

const clientPrincipal = await getAuthClientPrincipal(providerName, codeValue!, authConfigs);
const clientPrincipal = await getAuthClientPrincipal(providerName, codeValue!, clientId, clientSecret, openIdIssuer!);

if (clientPrincipal !== null && customAuth?.rolesSource) {
try {
Expand Down
Loading

0 comments on commit ed735e6

Please sign in to comment.