-
Notifications
You must be signed in to change notification settings - Fork 12k
chore: Add logging to SAML endpoints #22968
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,21 +2,32 @@ import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir"; | |
| import { parseRequestData } from "app/api/parseRequestData"; | ||
| import type { NextRequest } from "next/server"; | ||
| import { NextResponse } from "next/server"; | ||
| import { uuid } from "short-uuid"; | ||
|
|
||
| import jackson from "@calcom/features/ee/sso/lib/jackson"; | ||
| import type { SAMLResponsePayload } from "@calcom/features/ee/sso/lib/jackson"; | ||
| import logger from "@calcom/lib/logger"; | ||
|
|
||
| async function handler(req: NextRequest) { | ||
| const log = logger.getSubLogger({ prefix: ["[SAML callback]"] }); | ||
| const { oauthController } = await jackson(); | ||
|
|
||
| const { redirect_url } = await oauthController.samlResponse( | ||
| (await parseRequestData(req)) as SAMLResponsePayload | ||
| ); | ||
| const requestData = (await parseRequestData(req)) as SAMLResponsePayload; | ||
|
|
||
| const { redirect_url, error } = await oauthController.samlResponse(requestData); | ||
|
|
||
| if (redirect_url) { | ||
| return NextResponse.redirect(redirect_url, 302); | ||
| } | ||
|
|
||
| if (error) { | ||
| const uid = uuid(); | ||
| log.error( | ||
| `Error authenticating user with error ${error} for relayState ${requestData?.RelayState} trace:${uid}` | ||
| ); | ||
| return NextResponse.json({ message: `Error authorizing user. trace: ${uid}` }, { status: 400 }); | ||
|
Comment on lines
+24
to
+28
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we don't have any identifying information here, let's add a trace that the user can use when reaching out to support. |
||
| } | ||
|
|
||
| return NextResponse.json({ message: "No redirect URL provided" }, { status: 400 }); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,34 +1,52 @@ | ||||||||||||||||||||||||||||||||||||||||
| import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir"; | ||||||||||||||||||||||||||||||||||||||||
| import type { NextRequest } from "next/server"; | ||||||||||||||||||||||||||||||||||||||||
| import { NextResponse } from "next/server"; | ||||||||||||||||||||||||||||||||||||||||
| import z from "zod"; | ||||||||||||||||||||||||||||||||||||||||
| import { uuid } from "short-uuid"; | ||||||||||||||||||||||||||||||||||||||||
| import { z } from "zod"; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| import jackson from "@calcom/features/ee/sso/lib/jackson"; | ||||||||||||||||||||||||||||||||||||||||
| import { HttpError } from "@calcom/lib/http-error"; | ||||||||||||||||||||||||||||||||||||||||
| import logger from "@calcom/lib/logger"; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| const extractAuthToken = (req: NextRequest) => { | ||||||||||||||||||||||||||||||||||||||||
| const log = logger.getSubLogger({ prefix: ["SAML extractAuthToken"] }); | ||||||||||||||||||||||||||||||||||||||||
| const uid = uuid(); | ||||||||||||||||||||||||||||||||||||||||
| const authHeader = req.headers.get("authorization"); | ||||||||||||||||||||||||||||||||||||||||
| const parts = (authHeader || "").split(" "); | ||||||||||||||||||||||||||||||||||||||||
| if (parts.length > 1) return parts[1]; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| // check for query param | ||||||||||||||||||||||||||||||||||||||||
| let arr: string[] = []; | ||||||||||||||||||||||||||||||||||||||||
| const { access_token } = requestQuery.parse(Object.fromEntries(req.nextUrl.searchParams)); | ||||||||||||||||||||||||||||||||||||||||
| const tokenParse = requestQuery.safeParse(Object.fromEntries(req.nextUrl.searchParams)); | ||||||||||||||||||||||||||||||||||||||||
| let access_token; | ||||||||||||||||||||||||||||||||||||||||
| if (!tokenParse.success) { | ||||||||||||||||||||||||||||||||||||||||
| log.error(`Error parsing request query: ${tokenParse.error} trace ${uid}`); | ||||||||||||||||||||||||||||||||||||||||
| throw new HttpError({ statusCode: 401, message: `Unauthorized trace: ${uid}` }); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| access_token = tokenParse.data.access_token; | ||||||||||||||||||||||||||||||||||||||||
| arr = arr.concat(access_token); | ||||||||||||||||||||||||||||||||||||||||
| if (arr[0].length > 0) return arr[0]; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| throw new HttpError({ statusCode: 401, message: "Unauthorized" }); | ||||||||||||||||||||||||||||||||||||||||
| throw new HttpError({ statusCode: 401, message: `Unauthorized trace: ${uid}` }); | ||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| const requestQuery = z.object({ | ||||||||||||||||||||||||||||||||||||||||
| access_token: z.string(), | ||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| async function handler(req: NextRequest) { | ||||||||||||||||||||||||||||||||||||||||
| const log = logger.getSubLogger({ prefix: ["SAML userinfo"] }); | ||||||||||||||||||||||||||||||||||||||||
| const { oauthController } = await jackson(); | ||||||||||||||||||||||||||||||||||||||||
| const token = extractAuthToken(req); | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
39
to
40
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about we create traceId once here and pass the traceId to extractAuthToken too. That makes the traceId more usable. |
||||||||||||||||||||||||||||||||||||||||
| const userInfo = await oauthController.userInfo(token); | ||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json(userInfo); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||
| const userInfo = await oauthController.userInfo(token); | ||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json(userInfo); | ||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||
| const uid = uuid(); | ||||||||||||||||||||||||||||||||||||||||
hariombalhara marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||
| log.error(`trace: ${uid} Error getting user info from token: ${error}`); | ||||||||||||||||||||||||||||||||||||||||
| throw new Error(`Error getting user info from token. trace: ${uid}`); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+42
to
+49
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix error handling - don't throw unhandled errors. The error handling throws a new Error after logging, which will result in an unhandled error in the API route. The error should be returned as an HTTP response instead. Apply this diff to properly handle the error: try {
const userInfo = await oauthController.userInfo(token);
return NextResponse.json(userInfo);
} catch (error) {
const uid = uuid();
log.error(`trace: ${uid} Error getting user info from token: ${error}`);
- throw new Error(`Error getting user info from token. trace: ${uid}`);
+ return NextResponse.json(
+ { message: `Error getting user info from token. trace: ${uid}` },
+ { status: 500 }
+ );
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| export const GET = defaultResponderForAppDir(handler); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tenant contains the org id