diff --git a/apps/account/src/lib/external-oidc/findAccount.ts b/apps/account/src/lib/external-oidc/findAccount.ts new file mode 100644 index 0000000..843edfb --- /dev/null +++ b/apps/account/src/lib/external-oidc/findAccount.ts @@ -0,0 +1,71 @@ +import { KoaContextWithOIDC, Account, ClaimsParameterMember, FindAccount, AccountClaims } from 'oidc-provider'; +import { fdb } from '../googlecloud/db'; + +interface LocalAccount { + id: string; + displayName?: string; + email?: string; + verifiedEmails?: string[]; // Array of verified emails + phone?: string; + verifiedPhones?: string[]; // Array of verified phones + birthdate?: string; + firstName?: string; + lastName?: string; + locale?: string; + photoURL?: string; +} + +async function retrieveAccountById(id: string): Promise { + try { + const snapshot = await fdb.doc(`users/${id}`).get(); + const accountData = snapshot.data(); + + if (!accountData) return null; + + return { + id, + displayName: accountData.displayName, + email: accountData.email, + verifiedEmails: accountData.verifiedEmails, + phone: accountData.phone, + verifiedPhones: accountData.verifiedPhones, + birthdate: accountData.birthdate, + firstName: accountData.firstName, + lastName: accountData.lastName, + locale: accountData.locale, + photoURL: accountData.photoURL, + }; + } catch (error) { + console.error(`Error retrieving account with ID ${id}:`, error); + return null; + } +} + +async function findAccount(ctx: KoaContextWithOIDC, id: string): Promise { + const account = await retrieveAccountById(id); + + if (!account) return undefined; + + return { + accountId: account.id, + async claims(use: string, scope: string): Promise { + const claims: AccountClaims = { + sub: account.id ?? id, + id: account.id ?? id, + displayName: account.displayName, + email: account.email, + verifiedEmails: account.verifiedEmails, + phone: account.phone, + verifiedPhones: account.verifiedPhones, + birthdate: account.birthdate, + firstName: account.firstName, + lastName: account.lastName, + locale: account.locale, + photoURL: account.photoURL, + }; + return claims; + }, + }; +} + +export default findAccount; diff --git a/apps/account/src/lib/external-oidc/google-cloud-adapter.ts b/apps/account/src/lib/external-oidc/google-cloud-adapter.ts index cf2d1b7..3f6249a 100644 --- a/apps/account/src/lib/external-oidc/google-cloud-adapter.ts +++ b/apps/account/src/lib/external-oidc/google-cloud-adapter.ts @@ -2,6 +2,10 @@ import { Adapter, AdapterPayload } from 'oidc-provider'; import { fdb, rdb } from '../googlecloud/db'; +function sanitizePayload(payload: Record): Record { + return JSON.parse(JSON.stringify(payload)); // Serializing removes undefined values +} + export default class GoogleCloudAdapter implements Adapter { private name: string; @@ -13,9 +17,10 @@ export default class GoogleCloudAdapter implements Adapter { * Save data to Firebase with expiration. */ async upsert(id: string, payload: AdapterPayload, expiresIn: number): Promise { + const sanitizedPayload = sanitizePayload(payload); // Remove undefined values const ref = rdb.ref(`${this.name}/${id}`); await ref.set({ - ...payload, + ...sanitizedPayload, expiresAt: Date.now() + expiresIn * 1000, }); ref.onDisconnect().remove(); // Ensures data removal if the client disconnects @@ -135,26 +140,4 @@ export default class GoogleCloudAdapter implements Adapter { await rdb.ref().update(updates); } - - /** - * Find an account by ID. Uses Firestore as the data source. - */ - async findAccount(ctx: any, id: string): Promise<{ accountId: string; claims: () => Promise } | undefined> { - const snapshot = await fdb.doc(`users/${id}`).get(); - const accountData = snapshot.data(); - - if (!accountData) return undefined; - - return { - accountId: id, - claims: async () => ({ - sub: id, - uid: accountData.uid, - displayName: accountData.displayName, - email: accountData.email, - photoURL: accountData.photoURL, - emailVerified: accountData.email_verified, - }), - }; - } } diff --git a/apps/account/src/lib/external-oidc/index.ts b/apps/account/src/lib/external-oidc/index.ts index cec5967..84ae761 100644 --- a/apps/account/src/lib/external-oidc/index.ts +++ b/apps/account/src/lib/external-oidc/index.ts @@ -1,24 +1,23 @@ // lib/oidcProvider.ts -import { Provider } from 'oidc-provider'; -import GoogleCloudAdapter from './google-cloud-adapter'; -import subscribe from './events-listeners.js' -const baseURL = process.env.APP_URL +import { Provider, KoaContextWithOIDC, Account } from 'oidc-provider'; +import GoogleCloudAdapter from './google-cloud-adapter'; +import subscribe from './events-listeners.js'; +import findAccount from './findAccount'; +/** + * Configures and creates an OIDC provider instance with custom options. + * This provider handles user authentication, token management, and more. + */ export function createOidcProvider() { const oidc = new Provider('https://account.eartho.io', { + // Adapter for data persistence in Google Cloud adapter: GoogleCloudAdapter, + + // No pre-configured clients, clients should be registered as needed clients: [], - formats: { - customizers: { - jwt: (ctx, token, parts) => { - if (token.kind === 'AccessToken') { - return { ...parts, header: { ...parts.header, alg: 'RS256' }, payload: { ...parts.payload } }; - } - return parts; - }, - }, - }, + + // Define routes for various OIDC endpoints routes: { authorization: '/api/oidc/auth', token: '/api/oidc/token', @@ -30,6 +29,8 @@ export function createOidcProvider() { end_session: '/api/oidc/session/end', device_authorization: '/api/oidc/device/code', }, + + // Default settings for client configurations clientDefaults: { grant_types: [ 'authorization_code', @@ -37,95 +38,95 @@ export function createOidcProvider() { 'client_credentials', 'urn:ietf:params:oauth:grant-type:device_code', 'urn:ietf:params:oauth:grant-type:jwt-bearer', - 'implicit' + 'implicit', ], response_types: ['code', 'id_token'], }, - clientBasedCORS(ctx, origin, client) { - return true; - }, - features: { - devInteractions: { enabled: false }, + // Allow CORS requests from any origin + clientBasedCORS: () => true, - deviceFlow: { enabled: true }, + // Enable and configure OIDC features + features: { + devInteractions: { enabled: false }, + deviceFlow: { enabled: true }, revocation: { enabled: true }, introspection: { enabled: true }, webMessageResponseMode: { enabled: true }, - claimsParameter: { enabled: true } + claimsParameter: { enabled: true }, }, + + // Enables rotating refresh tokens rotateRefreshToken: true, + + // Defines the available claims and their scopes claims: { address: ['address'], email: ['email', 'verifiedEmails'], phone: ['phone', 'verifiedPhone'], profile: ['id', 'birthdate', 'firstName', 'gender', 'lastName', 'locale', 'displayName', 'photoURL'], }, + // Define extra parameters for handling scope dynamically + extraParams: { + scope(ctx, value, client) { + if (ctx.oidc.params) { + // Set default scope if not provided + ctx.oidc.params.scope ||= value || client.scope || 'openid profile email'; + } + }, + }, + // Interaction URLs, with client_id and interaction UID as parameters interactions: { - url: (ctx, interaction): string => { + url: (ctx: KoaContextWithOIDC, interaction): string => { const params = new URLSearchParams(); - const clientId = ctx.oidc.params?.client_id; - if (typeof clientId === 'string') { - params.set('client_id', clientId); - } - - // Add the interaction UID as a parameter + if (clientId) params.set('client_id', clientId.toString()); params.set('interaction', interaction.uid); - - // Construct the final URL with all query parameters return `/connect/${interaction.prompt.name}?${params.toString()}`; }, }, - findAccount: async (ctx, id) => { - // This should be replaced with actual account retrieval logic - return { - accountId: id, - async claims(use, scope) { - return { sub: id, name: 'Example User', email: 'user@example.com' }; - }, - }; - }, + // Reference to the account retrieval logic + findAccount, + + // Cookie configuration for session management cookies: { - long: { signed: true, secure: false, path: '/', sameSite: 'lax' }, - short: { signed: true, secure: false, path: '/', sameSite: 'lax' }, - keys: [process.env.COOKIE_SECRET!], + long: { signed: true, secure: process.env.NODE_ENV === 'production', path: '/', sameSite: 'lax' }, + short: { signed: true, secure: process.env.NODE_ENV === 'production', path: '/', sameSite: 'lax' }, + keys: [process.env.COOKIE_SECRET ?? (() => { throw new Error("Missing COOKIE_SECRET") })()], }, + + // JSON Web Key Set (JWKS) for token signing, loaded from environment variables jwks: { keys: [ { kty: 'RSA', - kid: process.env.JWKS_KID, // Load from environment + kid: process.env.JWKS_KID!, alg: 'RS256', use: 'sig', e: 'AQAB', - n: process.env.JWKS_PUBLIC_KEY, // Load public key modulus from environment - d: process.env.JWKS_PRIVATE_KEY_D, // Private key component - p: process.env.JWKS_PRIVATE_KEY_P, // Prime factor p - q: process.env.JWKS_PRIVATE_KEY_Q, // Prime factor q - dp: process.env.JWKS_PRIVATE_KEY_DP, - dq: process.env.JWKS_PRIVATE_KEY_DQ, - qi: process.env.JWKS_PRIVATE_KEY_QI, + n: process.env.JWKS_PUBLIC_KEY!, + d: process.env.JWKS_PRIVATE_KEY_D!, + p: process.env.JWKS_PRIVATE_KEY_P!, + q: process.env.JWKS_PRIVATE_KEY_Q!, + dp: process.env.JWKS_PRIVATE_KEY_DP!, + dq: process.env.JWKS_PRIVATE_KEY_DQ!, + qi: process.env.JWKS_PRIVATE_KEY_QI!, }, ], }, }); - subscribe(oidc); + // Uncomment the following line to attach event listeners, if needed + // subscribe(oidc); - const { invalidate: orig } = oidc.Client.Schema.prototype; - - oidc.Client.Schema.prototype.invalidate = function invalidate(message, code) { - if (code === 'implicit-force-https' || code === 'implicit-forbid-localhost') { - return; + // Override the invalidate method to skip validation in specific cases + const { invalidate: originalInvalidate } = (oidc.Client as any).Schema.prototype; + (oidc.Client as any).Schema.prototype.invalidate = function (message: any, code: string) { + if (code !== 'implicit-force-https' && code !== 'implicit-forbid-localhost') { + originalInvalidate.call(this, message); } - - orig.call(this, message); }; - return oidc; } - - diff --git a/apps/account/src/middleware.tsx b/apps/account/src/middleware.tsx index 8a766ea..d48d676 100755 --- a/apps/account/src/middleware.tsx +++ b/apps/account/src/middleware.tsx @@ -5,6 +5,11 @@ import { SUPPORTED_LOCALES } from './i18n'; export function middleware(req: NextRequest) { const url = req.nextUrl.clone(); + // Skip middleware for API routes + if (url.pathname.startsWith('/api') || url.pathname.startsWith('/.well-known')) { + return NextResponse.next(); + } + const acceptLanguage = req.headers.get('accept-language'); const cookieLocale = req.cookies.get('NEXT_LOCALE')?.value; @@ -17,7 +22,7 @@ export function middleware(req: NextRequest) { if(!SUPPORTED_LOCALES.includes(finalLocale)){ finalLocale = 'en'; } - const res = NextResponse.redirect(url); + const res = NextResponse.next(); res.cookies.set('NEXT_LOCALE', finalLocale, { maxAge: 60 * 60 * 24 * 365 }); // 1 year return res; } diff --git a/apps/account/src/pages/api/oidc/[...slug].ts b/apps/account/src/pages/api/oidc/[...slug].ts index 662bf34..9be093d 100644 --- a/apps/account/src/pages/api/oidc/[...slug].ts +++ b/apps/account/src/pages/api/oidc/[...slug].ts @@ -1,13 +1,13 @@ // pages/api/oidc/[...slug].ts import type { NextApiRequest, NextApiResponse } from 'next'; import { createOidcProvider } from '@/lib/external-oidc'; -import { auth } from '@/auth'; -import { serialize } from 'cookie'; const provider = createOidcProvider(); export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { + const { slug } = req.query; + // Set caching headers to disable caching res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); res.setHeader('Pragma', 'no-cache'); diff --git a/apps/myaccount/myaccount-web-app/package-lock.json b/apps/myaccount/myaccount-web-app/package-lock.json index 50cc750..aa46257 100755 --- a/apps/myaccount/myaccount-web-app/package-lock.json +++ b/apps/myaccount/myaccount-web-app/package-lock.json @@ -1,11 +1,10 @@ { - "name": "EarthoOne", + "name": "myaccount-web-app", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "EarthoOne", "version": "0.1.0", "dependencies": { "@faker-js/faker": "^8.4.1", @@ -55,11 +54,12 @@ "jsonwebtoken": "^9.0.2", "lucide-react": "^0.418.0", "luxon": "^3.4.4", - "next": "^14.2.4", + "next": "^14.2.17", "next-auth": "^5.0.0-beta.25", "next-i18next": "^15.3.0", "next-intl": "^3.15.2", "next-themes": "^0.3.0", + "node-fetch": "^3.3.2", "npm": "^10.8.1", "pino-pretty": "^11.2.1", "postcss": "8.4.38", @@ -4462,6 +4462,15 @@ "node": ">=12" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/date-fns": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", @@ -4625,15 +4634,6 @@ "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==" }, - "node_modules/encoding": { - "version": "0.1.13", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, "node_modules/end-of-stream": { "version": "1.4.4", "license": "MIT", @@ -4960,6 +4960,29 @@ "node": ">=0.8.0" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5078,6 +5101,18 @@ "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -5139,6 +5174,27 @@ "node": ">=14" } }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/gcp-metadata": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", @@ -5258,6 +5314,27 @@ "node": ">=14" } }, + "node_modules/google-gax/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -5472,23 +5549,31 @@ "node-fetch": "^2.6.12" } }, - "node_modules/i18next-resources-to-backend": { - "version": "1.2.1", + "node_modules/i18next-http-backend/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.2" + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/iconv-lite": { - "version": "0.6.3", + "node_modules/i18next-resources-to-backend": { + "version": "1.2.1", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" + "@babel/runtime": "^7.23.2" } }, "node_modules/ieee754": { @@ -6273,22 +6358,41 @@ "node": ">=10" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { - "version": "2.7.0", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "license": "MIT", "dependencies": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, "node_modules/node-forge": { @@ -9689,12 +9793,6 @@ ], "license": "MIT" }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -10144,6 +10242,27 @@ "node": ">= 6" } }, + "node_modules/teeny-request/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/text-table": { "version": "0.2.0", "license": "MIT" @@ -10182,6 +10301,8 @@ }, "node_modules/tr46": { "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, "node_modules/ts-interface-checker": { @@ -10452,8 +10573,19 @@ "node": ">=0.10.0" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, "node_modules/websocket-driver": { @@ -10479,6 +10611,8 @@ }, "node_modules/whatwg-url": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", "dependencies": { "tr46": "~0.0.3", diff --git a/apps/myaccount/myaccount-web-app/package.json b/apps/myaccount/myaccount-web-app/package.json index be112a1..377241f 100755 --- a/apps/myaccount/myaccount-web-app/package.json +++ b/apps/myaccount/myaccount-web-app/package.json @@ -1,5 +1,5 @@ { - "name": "EarthoOne", + "name": "", "version": "0.1.0", "private": true, "scripts": { @@ -62,11 +62,12 @@ "jsonwebtoken": "^9.0.2", "lucide-react": "^0.418.0", "luxon": "^3.4.4", - "next": "^14.2.4", + "next": "^14.2.17", "next-auth": "^5.0.0-beta.25", "next-i18next": "^15.3.0", "next-intl": "^3.15.2", "next-themes": "^0.3.0", + "node-fetch": "^3.3.2", "npm": "^10.8.1", "pino-pretty": "^11.2.1", "postcss": "8.4.38", diff --git a/apps/myaccount/myaccount-web-app/src/auth.ts b/apps/myaccount/myaccount-web-app/src/auth.ts index 9bdd3d7..8452f75 100755 --- a/apps/myaccount/myaccount-web-app/src/auth.ts +++ b/apps/myaccount/myaccount-web-app/src/auth.ts @@ -1,9 +1,7 @@ -import NextAuth, { Account, JWT, NextAuthConfig, Session } from 'next-auth'; +import NextAuth, { Account, customFetch, JWT, NextAuthConfig, Session } from 'next-auth'; import { redirect } from 'next/navigation'; import { getEarthoToken } from './lib/auth/earthotoken'; -import Apple from "next-auth/providers/apple" -import Discord from "next-auth/providers/discord" const TIME_TO_LIVE_SEC = 30 * 24 * 60 * 60; // 30 DAYS @@ -15,23 +13,22 @@ const authOptions: NextAuthConfig = { signIn: loginPage, error: '/auth/error', }, - debug: true, secret: process.env.AUTH_SECRET, session: { strategy: 'jwt', maxAge: TIME_TO_LIVE_SEC, // 30 days }, + callbacks: { async jwt({ token, user, account }) { - console.log(token); // Initial sign in if (account && user) { - const { id, uid, email, emailVerified, firstName, lastName, displayName, photoURL, verifiedEmails } = user as User; - const newUser = { id, uid, email, emailVerified, firstName, lastName, displayName, photoURL, verifiedEmails } as User; + const { id, uid, email, firstName, lastName, displayName, photoURL, verifiedEmails } = user as User; + const newUser = { id, uid, email, firstName, lastName, displayName, photoURL, verifiedEmails } as User; - token.accessToken = await getEarthoToken(newUser , account, TIME_TO_LIVE_SEC); + token.accessToken = await getEarthoToken(newUser, account, TIME_TO_LIVE_SEC); token.accessTokenExpires = Date.now() + TIME_TO_LIVE_SEC * 1000; token.refreshToken = account.refresh_token; token.user = newUser; @@ -54,30 +51,29 @@ const authOptions: NextAuthConfig = { return session; }, }, - - logger: { - error(code, ...message) { - console.error(code, message) - }, - warn(code, ...message) { - console.warn(code, message) - }, - debug(code, ...message) { - console.debug(code, message) - }, - }, - + // logger: { + // error(code, ...message) { + // console.error(code, message) + // }, + // warn(code, ...message) { + // console.warn(code, message) + // }, + // debug(code, ...message) { + // console.debug(code, message) + // }, + // }, providers: [ { - + id: "eartho", name: "Eartho", type: "oidc", - issuer: "https://account.eartho.io", clientId: process.env.EARTHO_ACCOUNT_CLIENT_ID, clientSecret: process.env.EARTHO_ACCOUNT_CLIENT_SECRET, + wellKnown: "https://account.eartho.io/.well-known/openid-configuration", + authorization: { url: "https://account.eartho.io/api/oidc/auth", params: { @@ -87,20 +83,8 @@ const authOptions: NextAuthConfig = { token: "https://account.eartho.io/api/oidc/token", userinfo: "https://account.eartho.io/api/oidc/userinfo", - // idToken: true, - idToken: true, + idToken: false, checks: ["pkce"], - client: { token_endpoint_auth_method: "client_secret_post" }, - - - profile(profile) { - return { - id: profile.sub, - name: profile.name, - email: profile.email, - image: profile.picture, - } - }, } ], };