From 49ba4b3bae05d4620f81cf308cb74d714d6147a5 Mon Sep 17 00:00:00 2001 From: Incorbador Date: Thu, 6 Feb 2025 09:31:42 +0100 Subject: [PATCH 1/2] Refresh stale login-init --- packages/web-core/src/services/ConnectService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web-core/src/services/ConnectService.ts b/packages/web-core/src/services/ConnectService.ts index 80ddaea90..44e49532d 100644 --- a/packages/web-core/src/services/ConnectService.ts +++ b/packages/web-core/src/services/ConnectService.ts @@ -225,8 +225,8 @@ export class ConnectService { connectToken?: string, ac?: AbortController, ): Promise> { - const existingProcess = await this.#getExistingProcess(() => this.loginInit(ac ?? new AbortController())); - if (!existingProcess) { + const existingProcess = await this.loginInit(ac ?? new AbortController()); + if (existingProcess.err) { return Err(CorbadoError.missingInit()); } From 09ad1b5a3787eb06eddca4c77a19a7608eeab165 Mon Sep 17 00:00:00 2001 From: Incorbador Date: Thu, 6 Feb 2025 09:45:44 +0100 Subject: [PATCH 2/2] Improve connect process client-side management --- .../src/components/login/LoginInitScreen.tsx | 1 + packages/web-core/openapi/spec_v2.yaml | 41 +++++++++- packages/web-core/src/api/v2/api.ts | 82 ++++++++++++++++++- .../web-core/src/services/ConnectService.ts | 6 +- 4 files changed, 126 insertions(+), 4 deletions(-) diff --git a/packages/connect-react/src/components/login/LoginInitScreen.tsx b/packages/connect-react/src/components/login/LoginInitScreen.tsx index e3985663f..b7efcbe7d 100644 --- a/packages/connect-react/src/components/login/LoginInitScreen.tsx +++ b/packages/connect-react/src/components/login/LoginInitScreen.tsx @@ -151,6 +151,7 @@ const LoginInitScreen: FC = ({ showFallback = false }) => { () => { return; }, + loadedMs, ); if (res.err) { diff --git a/packages/web-core/openapi/spec_v2.yaml b/packages/web-core/openapi/spec_v2.yaml index 3f783716c..f8914cba9 100644 --- a/packages/web-core/openapi/spec_v2.yaml +++ b/packages/web-core/openapi/spec_v2.yaml @@ -1137,6 +1137,8 @@ components: type: boolean shortSessionCookieConfig: $ref: '#/components/schemas/shortSessionCookieConfig' + sessionTokenCookieConfig: + $ref: '#/components/schemas/sessionTokenCookieConfig' frontendApiUrl: type: string @@ -1170,9 +1172,13 @@ components: type: object required: - shortSession + - sessionToken properties: shortSession: type: string + deprecated: true + sessionToken: + type: string mePasskeyDeleteRsp: type: object @@ -1266,6 +1272,9 @@ components: type: boolean assertionResponse: type: string + loadedMs: + type: integer + format: int64 connectLoginFinishRsp: type: object @@ -1592,6 +1601,28 @@ components: type: boolean shortSessionCookieConfig: + type: object + deprecated: true + required: + - domain + - secure + - sameSite + - path + - lifetimeSeconds + properties: + domain: + type: string + secure: + type: boolean + sameSite: + type: string + enum: [ 'lax', 'strict', 'none' ] + path: + type: string + lifetimeSeconds: + type: integer + + sessionTokenCookieConfig: type: object required: - domain @@ -1820,14 +1851,22 @@ components: required: - blockType - shortSession + - sessionToken properties: blockType: type: string longSession: type: string - description: Only given when project environment is dev + deprecated: true + description: This is only set if the project environment is set to 'dev'. If set the UI components will set the longSession in local storage because the cookie dropping will not work in Safari for example ("third-party cookie"). + refreshToken: + type: string + description: This is only set if the project environment is set to 'dev'. If set the UI components will set the longSession in local storage because the cookie dropping will not work in Safari for example ("third-party cookie"). shortSession: type: string + deprecated: true + sessionToken: + type: string passkeyOperation: $ref: '#/components/schemas/passkeyOperation' diff --git a/packages/web-core/src/api/v2/api.ts b/packages/web-core/src/api/v2/api.ts index adab9da74..5ac2a472e 100644 --- a/packages/web-core/src/api/v2/api.ts +++ b/packages/web-core/src/api/v2/api.ts @@ -445,6 +445,12 @@ export interface ConnectLoginFinishReq { * @memberof ConnectLoginFinishReq */ 'assertionResponse': string; + /** + * + * @type {number} + * @memberof ConnectLoginFinishReq + */ + 'loadedMs'?: number; } /** * @@ -855,17 +861,31 @@ export interface GeneralBlockCompleted { */ 'blockType': string; /** - * Only given when project environment is dev + * This is only set if the project environment is set to \'dev\'. If set the UI components will set the longSession in local storage because the cookie dropping will not work in Safari for example (\"third-party cookie\"). * @type {string} * @memberof GeneralBlockCompleted + * @deprecated */ 'longSession'?: string; + /** + * This is only set if the project environment is set to \'dev\'. If set the UI components will set the longSession in local storage because the cookie dropping will not work in Safari for example (\"third-party cookie\"). + * @type {string} + * @memberof GeneralBlockCompleted + */ + 'refreshToken'?: string; /** * * @type {string} * @memberof GeneralBlockCompleted + * @deprecated */ 'shortSession': string; + /** + * + * @type {string} + * @memberof GeneralBlockCompleted + */ + 'sessionToken': string; /** * * @type {PasskeyOperation} @@ -1644,8 +1664,15 @@ export interface MeRefreshRsp { * * @type {string} * @memberof MeRefreshRsp + * @deprecated */ 'shortSession': string; + /** + * + * @type {string} + * @memberof MeRefreshRsp + */ + 'sessionToken': string; } /** * @@ -2163,8 +2190,15 @@ export interface SessionConfigRsp { * * @type {ShortSessionCookieConfig} * @memberof SessionConfigRsp + * @deprecated */ 'shortSessionCookieConfig'?: ShortSessionCookieConfig; + /** + * + * @type {SessionTokenCookieConfig} + * @memberof SessionConfigRsp + */ + 'sessionTokenCookieConfig'?: SessionTokenCookieConfig; /** * * @type {string} @@ -2172,6 +2206,52 @@ export interface SessionConfigRsp { */ 'frontendApiUrl'?: string; } +/** + * + * @export + * @interface SessionTokenCookieConfig + */ +export interface SessionTokenCookieConfig { + /** + * + * @type {string} + * @memberof SessionTokenCookieConfig + */ + 'domain': string; + /** + * + * @type {boolean} + * @memberof SessionTokenCookieConfig + */ + 'secure': boolean; + /** + * + * @type {string} + * @memberof SessionTokenCookieConfig + */ + 'sameSite': SessionTokenCookieConfigSameSiteEnum; + /** + * + * @type {string} + * @memberof SessionTokenCookieConfig + */ + 'path': string; + /** + * + * @type {number} + * @memberof SessionTokenCookieConfig + */ + 'lifetimeSeconds': number; +} + +export const SessionTokenCookieConfigSameSiteEnum = { + Lax: 'lax', + Strict: 'strict', + None: 'none' +} as const; + +export type SessionTokenCookieConfigSameSiteEnum = typeof SessionTokenCookieConfigSameSiteEnum[keyof typeof SessionTokenCookieConfigSameSiteEnum]; + /** * * @export diff --git a/packages/web-core/src/services/ConnectService.ts b/packages/web-core/src/services/ConnectService.ts index 44e49532d..bff8163ab 100644 --- a/packages/web-core/src/services/ConnectService.ts +++ b/packages/web-core/src/services/ConnectService.ts @@ -279,6 +279,7 @@ export class ConnectService { preWebAuthn: (ac: AbortController) => void, postWebAuthn: () => void, onLoginEnd: () => void, + loadedMs: number, ): Promise> { const existingProcess = await this.#getExistingProcess(() => this.loginInit(new AbortController())); if (!existingProcess) { @@ -297,7 +298,7 @@ export class ConnectService { } postWebAuthn(); - const loginFinishResp = await this.#loginFinish(res.val, true); + const loginFinishResp = await this.#loginFinish(res.val, true, loadedMs); onLoginEnd(); return loginFinishResp; @@ -432,6 +433,7 @@ export class ConnectService { async #loginFinish( assertionResponse: string, isConditionalUI: boolean, + loadedMs?: number, ): Promise> { const existingProcess = await this.#getExistingProcess(() => this.loginInit(new AbortController())); if (!existingProcess) { @@ -439,7 +441,7 @@ export class ConnectService { } const res = await this.wrapWithErr(() => - this.#connectApi.connectLoginFinish({ assertionResponse, isConditionalUI }, { timeout: 15 * 1000 }), + this.#connectApi.connectLoginFinish({ assertionResponse, isConditionalUI, loadedMs }, { timeout: 15 * 1000 }), ); if (isConditionalUI) {