feat: OAuth 2.0 support for atoms (#27158)

* fix: useOAuthClient support OAuth 2.0

* fix: cannot read properties of undefined (reading NEXT_PUBLIC_IS_E2E)

* fix: allow OAuth 2.0 token to connect gcal or ms calendar

* fix: allow OAuth 2.0 token to save gcal or ms calendar credentials

* refactor: dont set oauth id header for OAuth 2.0

* fix: calendar events not showing and emails not sent

* feat: CalOAuth2Provider

* chore: make OAuth 2.0 work in examples app

* chore: refresh OAuth 2.0 tokens

* docs: running examples app with oauth 2.0

* fix: remove sensitive console.log statements that leak secrets

Remove logging of:
- OAuth authorization codes (oauth2-user.ts)
- Token-bearing exchange responses (oauth2-user.ts)
- /me response data containing PII (oauth2-user.ts)
- OAuth2 refresh response with tokens (refresh.ts)
- Response payload with access tokens (_app.tsx)

Addresses Cubic AI review feedback for issues with confidence >= 9/10

Co-Authored-By: unknown <>

* docs: update readme

* fix: implemente cubic feedback

* fix: seed script import

* fix: seed script pkce

* fix: correct typos and SQLite capitalization in OAuth2 README (#27176)

Co-authored-by: cubic-dev-ai[bot] <1082092+cubic-dev-ai[bot]@users.noreply.github.com>

* refactor: dont return name in public oauth endpoint

* docs: CalOAuthProvider

* chore: add NEXT_PUBLIC_IS_E2E constant to test

* docs: fix duplicated 'or' in Cal OAuth Provider documentation (#27177)

Co-authored-by: cubic-dev-ai[bot] <1082092+cubic-dev-ai[bot]@users.noreply.github.com>

* revert: is e2e constant

* fix: typecheck

* refactor: example app users select

* update readme

* chore: update oauth atoms readme

* refactor: enable booking managed event types with user.username instead of profile.username

* fix: EventTypeSettings when viewing round robin

* test: add e2e tests for atoms-oauth2 controller

Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>

* fix: correct error message path in atoms-oauth2 e2e test

Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <1082092+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in>
This commit is contained in:
Lauris Skraucis
2026-02-04 12:54:15 +01:00
committed by GitHub
co-authored by unknown cubic-dev-ai[bot] Devin AI cubic-dev-ai[bot] Rajiv Sahal
parent 0776bdf5fe
commit fc602d3b03
27 changed files with 663 additions and 65 deletions
+22
View File
@@ -188,6 +188,28 @@ export async function createUserAndEventType({
return theUser;
}
type OAuthClientInput = {
clientId: string;
clientSecret: string;
name: string;
purpose: string;
redirectUri: string;
websiteUrl: string;
enablePkce: boolean;
};
export async function createOAuthClientForUser(userId: number, oAuthClient: OAuthClientInput) {
const {enablePkce, ...restOfOAuthClient} = oAuthClient;
await prisma.oAuthClient.create({
data: {
userId,
...restOfOAuthClient,
clientType: enablePkce ? "PUBLIC" : "CONFIDENTIAL",
},
});
console.log(`\t👤 Created OAuth2 client '${oAuthClient.name}' for user with id '${userId}'`);
}
export async function createTeamAndAddUsers(
teamInput: Prisma.TeamCreateInput,
users: { id: number; username: string; role?: MembershipRole }[] = []
+17 -2
View File
@@ -17,7 +17,7 @@ import type z from "zod";
import type { teamMetadataSchema } from "../packages/prisma/zod-utils";
import mainAppStore from "./seed-app-store";
import mainHugeEventTypesSeed from "./seed-huge-event-types";
import { createUserAndEventType } from "./seed-utils";
import { createOAuthClientForUser, createUserAndEventType } from "./seed-utils";
type PlatformUser = {
email: string;
@@ -1036,7 +1036,7 @@ async function main() {
},
});
await createUserAndEventType({
const admin = await createUserAndEventType({
user: {
email: "admin@example.com",
/** To comply with admin password requirements */
@@ -1047,6 +1047,21 @@ async function main() {
},
});
const clientId = process.env.SEED_OAUTH2_CLIENT_ID;
const clientSecret = process.env.SEED_OAUTH2_CLIENT_SECRET_HASHED;
if (clientId && clientSecret) {
await createOAuthClientForUser(admin.id, {
clientId,
clientSecret,
name: "atoms examples app oauth 2 client",
purpose: "test atoms examples app with oauth 2",
redirectUri: "http://localhost:4321",
websiteUrl: "http://localhost:4321",
enablePkce: false,
});
}
await createPlatformAndSetupUser({
teamInput: {
name: "Platform Team",