Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: OAuth2 fixes and test-server updates #871

Merged
merged 1 commit into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/build/querier.js

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

21 changes: 19 additions & 2 deletions lib/build/recipe/oauth2/api/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
var __importDefault =
(this && this.__importDefault) ||
function (mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../../../utils");
const set_cookie_parser_1 = __importDefault(require("set-cookie-parser"));
async function authGET(apiImplementation, options, userContext) {
if (apiImplementation.authGET === undefined) {
return false;
Expand All @@ -31,8 +37,19 @@ async function authGET(apiImplementation, options, userContext) {
if ("redirectTo" in response) {
// TODO:
if (response.setCookie) {
for (const c of response.setCookie.replace(/, (\w+=)/, "\n$1").split("\n")) {
options.res.setHeader("set-cookie", c, true);
const cookieStr = set_cookie_parser_1.default.splitCookiesString(response.setCookie);
const cookies = set_cookie_parser_1.default.parse(cookieStr);
for (const cookie of cookies) {
options.res.setCookie(
cookie.name,
cookie.value,
cookie.domain,
!!cookie.secure,
!!cookie.httpOnly,
new Date(cookie.expires).getTime(),
cookie.path || "/",
cookie.sameSite
);
}
}
options.res.original.redirect(response.redirectTo);
Expand Down
14 changes: 8 additions & 6 deletions lib/build/recipe/oauth2/recipeImplementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,20 +257,22 @@ function getRecipeInterface(querier, _config, _appInfo) {
);
},
getOAuth2Clients: async function (input, userContext) {
let response = await querier.sendGetRequest(
var _a;
let response = await querier.sendGetRequestWithResponseHeaders(
new normalisedURLPath_1.default(`/recipe/oauth2/admin/clients`),
Object.assign(Object.assign({}, utils_1.transformObjectKeys(input, "snake-case")), {
page_token: input.paginationToken,
}),
{},
userContext
);
if (response.status === "OK") {
if (response.body.status === "OK") {
// Pagination info is in the Link header, containing comma-separated links:
// "first", "next" (if applicable).
// Example: Link: </admin/clients?page_size=5&page_token=token1>; rel="first", </admin/clients?page_size=5&page_token=token2>; rel="next"
// We parse the nextPaginationToken from the Link header using RegExp
let nextPaginationToken;
const linkHeader = response.headers.get("link");
const linkHeader = (_a = response.headers.get("link")) !== null && _a !== void 0 ? _a : "";
const nextLinkMatch = linkHeader.match(/<([^>]+)>;\s*rel="next"/);
if (nextLinkMatch) {
const url = nextLinkMatch[1];
Expand All @@ -279,14 +281,14 @@ function getRecipeInterface(querier, _config, _appInfo) {
}
return {
status: "OK",
clients: response.data.map((client) => OAuth2Client_1.OAuth2Client.fromAPIResponse(client)),
clients: response.body.data.map((client) => OAuth2Client_1.OAuth2Client.fromAPIResponse(client)),
nextPaginationToken,
};
} else {
return {
status: "ERROR",
error: response.data.error,
errorHint: response.data.errorHint,
error: response.body.data.error,
errorHint: response.body.data.errorHint,
};
}
},
Expand Down
3 changes: 2 additions & 1 deletion lib/ts/querier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -567,11 +567,13 @@ export class Querier {

if (strPath.startsWith(hydraPubPathPrefix)) {
currentDomain = hydraPubDomain;
currentBasePath = "";
strPath = strPath.replace(hydraPubPathPrefix, "/oauth2");
}

if (strPath.startsWith(hydraAdmPathPrefix)) {
currentDomain = hydraAdmDomain;
currentBasePath = "";
strPath = strPath.replace(hydraAdmPathPrefix, "/admin");
}

Expand Down Expand Up @@ -653,7 +655,6 @@ export class Querier {
}

async function handleHydraAPICall(response: Response) {
console.log({ hydraResponse: response, text: await response.clone().text() });
const contentType = response.headers.get("Content-Type");

if (contentType?.startsWith("application/json")) {
Expand Down
16 changes: 14 additions & 2 deletions lib/ts/recipe/oauth2/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import { send200Response } from "../../../utils";
import { APIInterface, APIOptions } from "..";
import { UserContext } from "../../../types";
import setCookieParser from "set-cookie-parser";

export default async function authGET(
apiImplementation: APIInterface,
Expand All @@ -38,8 +39,19 @@ export default async function authGET(
if ("redirectTo" in response) {
// TODO:
if (response.setCookie) {
for (const c of response.setCookie.replace(/, (\w+=)/, "\n$1").split("\n")) {
options.res.setHeader("set-cookie", c, true);
const cookieStr = setCookieParser.splitCookiesString(response.setCookie);
const cookies = setCookieParser.parse(cookieStr);
for (const cookie of cookies) {
options.res.setCookie(
cookie.name,
cookie.value,
cookie.domain,
!!cookie.secure,
!!cookie.httpOnly,
new Date(cookie.expires!).getTime(),
cookie.path || "/",
cookie.sameSite as any
);
}
}
options.res.original.redirect(response.redirectTo);
Expand Down
13 changes: 7 additions & 6 deletions lib/ts/recipe/oauth2/recipeImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,23 +265,24 @@ export default function getRecipeInterface(
},

getOAuth2Clients: async function (input, userContext) {
let response = await querier.sendGetRequest(
let response = await querier.sendGetRequestWithResponseHeaders(
new NormalisedURLPath(`/recipe/oauth2/admin/clients`),
{
...transformObjectKeys(input, "snake-case"),
page_token: input.paginationToken,
},
{},
userContext
);

if (response.status === "OK") {
if (response.body.status === "OK") {
// Pagination info is in the Link header, containing comma-separated links:
// "first", "next" (if applicable).
// Example: Link: </admin/clients?page_size=5&page_token=token1>; rel="first", </admin/clients?page_size=5&page_token=token2>; rel="next"

// We parse the nextPaginationToken from the Link header using RegExp
let nextPaginationToken: string | undefined;
const linkHeader = response.headers.get("link");
const linkHeader = response.headers.get("link") ?? "";

const nextLinkMatch = linkHeader.match(/<([^>]+)>;\s*rel="next"/);
if (nextLinkMatch) {
Expand All @@ -292,14 +293,14 @@ export default function getRecipeInterface(

return {
status: "OK",
clients: response.data.map((client: any) => OAuth2Client.fromAPIResponse(client)),
clients: response.body.data.map((client: any) => OAuth2Client.fromAPIResponse(client)),
nextPaginationToken,
};
} else {
return {
status: "ERROR",
error: response.data.error,
errorHint: response.data.errorHint,
error: response.body.data.error,
errorHint: response.body.data.errorHint,
};
}
},
Expand Down
26 changes: 22 additions & 4 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
"nodemailer": "^6.7.2",
"pkce-challenge": "^3.0.0",
"psl": "1.8.0",
"set-cookie-parser": "^2.6.0",
"supertokens-js-override": "^0.0.4",
"twilio": "^4.19.3"
},
Expand All @@ -143,6 +144,7 @@
"@types/koa-bodyparser": "^4.3.3",
"@types/nodemailer": "^6.4.4",
"@types/psl": "1.1.0",
"@types/set-cookie-parser": "^2.4.9",
"@types/validator": "10.11.0",
"aws-sdk-mock": "^5.4.0",
"body-parser": "1.20.1",
Expand Down
24 changes: 24 additions & 0 deletions test/test-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import ThirdPartyRecipe from "../../../lib/build/recipe/thirdparty/recipe";
import { TypeInput as ThirdPartyTypeInput } from "../../../lib/build/recipe/thirdparty/types";
import { TypeInput as MFATypeInput } from "../../../lib/build/recipe/multifactorauth/types";
import TOTPRecipe from "../../../lib/build/recipe/totp/recipe";
import OAuth2Recipe from "../../../lib/build/recipe/oauth2/recipe";
import { TypeInput as OAuth2TypeInput } from "../../../lib/build/recipe/oauth2/types";
import UserMetadataRecipe from "../../../lib/build/recipe/usermetadata/recipe";
import SuperTokensRecipe from "../../../lib/build/supertokens";
import { RecipeListFunction } from "../../../lib/build/types";
Expand All @@ -32,13 +34,15 @@ import Session from "../../../recipe/session";
import { verifySession } from "../../../recipe/session/framework/express";
import ThirdParty from "../../../recipe/thirdparty";
import TOTP from "../../../recipe/totp";
import OAuth2 from "../../../recipe/oauth2";
import accountlinkingRoutes from "./accountlinking";
import emailpasswordRoutes from "./emailpassword";
import emailverificationRoutes from "./emailverification";
import { logger } from "./logger";
import multiFactorAuthRoutes from "./multifactorauth";
import multitenancyRoutes from "./multitenancy";
import passwordlessRoutes from "./passwordless";
import oAuth2Routes from "./oauth2";
import sessionRoutes from "./session";
import supertokensRoutes from "./supertokens";
import thirdPartyRoutes from "./thirdparty";
Expand Down Expand Up @@ -81,6 +85,7 @@ function STReset() {
ProcessState.getInstance().reset();
MultiFactorAuthRecipe.reset();
TOTPRecipe.reset();
OAuth2Recipe.reset();
SuperTokensRecipe.reset();
}

Expand Down Expand Up @@ -237,6 +242,24 @@ function initST(config: any) {
if (recipe.recipeId === "totp") {
recipeList.push(TOTP.init(config));
}
if (recipe.recipeId === "oauth2") {
let initConfig: OAuth2TypeInput = {
...config,
};
if (initConfig.override?.functions) {
initConfig.override = {
...initConfig.override,
functions: getFunc(`${initConfig.override.functions}`),
};
}
if (initConfig.override?.apis) {
initConfig.override = {
...initConfig.override,
apis: getFunc(`${initConfig.override.apis}`),
};
}
recipeList.push(OAuth2.init(initConfig));
}
});

settings.recipeList = recipeList;
Expand Down Expand Up @@ -318,6 +341,7 @@ app.use("/test/multifactorauth", multiFactorAuthRoutes);
app.use("/test/thirdparty", thirdPartyRoutes);
app.use("/test/totp", TOTPRoutes);
app.use("/test/usermetadata", userMetadataRoutes);
app.use("/test/oauth2", oAuth2Routes);

// *** Custom routes to help with session tests ***
app.post("/create", async (req, res, next) => {
Expand Down
46 changes: 46 additions & 0 deletions test/test-server/src/oauth2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Router } from "express";
import OAuth2 from "../../../recipe/oauth2";
import { logger } from "./logger";

const namespace = "com.supertokens:node-test-server:oauth2";
const { logDebugMessage } = logger(namespace);

const router = Router()
.post("/getoauth2clients", async (req, res, next) => {
try {
logDebugMessage("OAuth2:getOAuth2Clients %j", req.body);
const response = await OAuth2.getOAuth2Clients(req.body.input, req.body.userContext);
res.json(response);
} catch (e) {
next(e);
}
})
.post("/createoauth2client", async (req, res, next) => {
try {
logDebugMessage("OAuth2:createOAuth2Client %j", req.body);
const response = await OAuth2.createOAuth2Client(req.body.input, req.body.userContext);
res.json(response);
} catch (e) {
next(e);
}
})
.post("/updateoauth2client", async (req, res, next) => {
try {
logDebugMessage("OAuth2:updateOAuth2Client %j", req.body);
const response = await OAuth2.updateOAuth2Client(req.body.input, req.body.userContext);
res.json(response);
} catch (e) {
next(e);
}
})
.post("/deleteoauth2client", async (req, res, next) => {
try {
logDebugMessage("OAuth2:deleteOAuth2Client %j", req.body);
const response = await OAuth2.deleteOAuth2Client(req.body.input, req.body.userContext);
res.json(response);
} catch (e) {
next(e);
}
});

export default router;
Loading