Skip to content
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
4 changes: 2 additions & 2 deletions .github/workflows/pr-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
run: pnpm format:check

- name: 🧹 Lint
run: pnpm lint
run: pnpm --filter '!./packages/**' lint

- name: 🧪 Test
run: pnpm test
run: pnpm --filter '!./packages/**' test
9 changes: 0 additions & 9 deletions packages/browser/src/ThunderIDBrowserClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import {
Storage,
TokenResponse,
extractPkceStorageKeyFromState,
initializeEmbeddedSignInFlow,
HttpError,
HttpRequestConfig,
HttpResponse,
Expand Down Expand Up @@ -301,14 +300,6 @@ class ThunderIDBrowserClient<T = BrowserAuthConfig> extends ThunderIDJavaScriptC
(sm as any).setTemporaryDataParameter(TOKEN_REQUEST_CONFIG_KEY, JSON.stringify(tokenRequestConfig));
}

if ((signInConfig as any)?.response_mode === 'direct') {
const authorizeUrl: URL = new URL(url);
return initializeEmbeddedSignInFlow({
url: `${authorizeUrl.origin}${authorizeUrl.pathname}`,
payload: Object.fromEntries(authorizeUrl.searchParams.entries()),
});
}

location.href = url;
await SPAUtils.waitTillPageRedirect();

Expand Down
2 changes: 1 addition & 1 deletion packages/browser/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export {default as hasCalledForThisInstanceInUrl} from './utils/hasCalledForThis
export {default as navigate} from './utils/navigate';
export {default as http} from './utils/http';
export {default as handleWebAuthnAuthentication} from './utils/handleWebAuthnAuthentication';
export {default as resolveEmojiUrisInHtml} from './utils/v2/resolveEmojiUrisInHtml';
export {default as resolveEmojiUrisInHtml} from './utils/resolveEmojiUrisInHtml';

// Theme
export {detectThemeMode, createClassObserver, createMediaQueryListener} from './theme/themeDetection';
Expand Down
4 changes: 2 additions & 2 deletions packages/express/src/middleware/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* under the License.
*/

import {executeEmbeddedSignInFlowV2, logger as Logger} from '@thunderid/node';
import {executeEmbeddedSignInFlow, logger as Logger} from '@thunderid/node';
import express from 'express';
import ThunderIDExpressClient from '../ThunderIDExpressClient';

Expand Down Expand Up @@ -82,7 +82,7 @@ const handleFlow = (): express.RequestHandler => {
? {action: 'submit', challengeToken, executionId, inputs}
: {applicationId, flowType: flowType ?? 'SIGN_IN'};

const flowResponse = await executeEmbeddedSignInFlowV2({
const flowResponse = await executeEmbeddedSignInFlow({
authId: resolvedAuthId,
baseUrl: baseUrl,
payload,
Expand Down
14 changes: 2 additions & 12 deletions packages/javascript/src/StorageManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,7 @@ class StorageManager<T> {

protected async setValue(
key: string,
attribute:
| keyof AuthClientConfig<T>
| keyof OIDCDiscoveryApiResponse
| keyof SessionData
| keyof TemporaryStore
| keyof HybridStore,
attribute: keyof AuthClientConfig<T> | keyof OIDCDiscoveryApiResponse | keyof SessionData | keyof TemporaryStore,
value: TemporaryStoreValue,
): Promise<void> {
const existingDataJSON: string = (await this.store.getData(key)) ?? null;
Expand All @@ -69,12 +64,7 @@ class StorageManager<T> {

protected async removeValue(
key: string,
attribute:
| keyof AuthClientConfig<T>
| keyof OIDCDiscoveryApiResponse
| keyof SessionData
| keyof TemporaryStore
| keyof HybridStore,
attribute: keyof AuthClientConfig<T> | keyof OIDCDiscoveryApiResponse | keyof SessionData | keyof TemporaryStore,
): Promise<void> {
const existingDataJSON: string = (await this.store.getData(key)) ?? null;
const existingData: PartialData<T> = existingDataJSON && JSON.parse(existingDataJSON);
Expand Down
81 changes: 3 additions & 78 deletions packages/javascript/src/ThunderIDJavaScriptClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
* under the License.
*/

import executeEmbeddedSignInFlow from './api/executeEmbeddedSignInFlow';
import initializeEmbeddedSignInFlow from './api/initializeEmbeddedSignInFlow';
import OIDCDiscoveryConstants from './constants/OIDCDiscoveryConstants';
import OIDCRequestConstants from './constants/OIDCRequestConstants';
import PKCEConstants from './constants/PKCEConstants';
Expand All @@ -32,17 +30,6 @@ import type {CIBAInitiateOptions, CIBAInitiateResponse, CIBAPollOptions} from '.
import {ThunderIDClient} from './models/client';
import {AuthClientConfig, Config, SignInOptions, SignOutOptions, SignUpOptions} from './models/config';
import {Crypto} from './models/crypto';
import {
EmbeddedFlowExecuteRequestConfig,
EmbeddedFlowExecuteRequestPayload,
EmbeddedFlowExecuteResponse,
} from './models/embedded-flow';
import {
EmbeddedSignInFlowAuthenticator,
EmbeddedSignInFlowHandleResponse,
EmbeddedSignInFlowInitiateResponse,
EmbeddedSignInFlowStatus,
} from './models/embedded-signin-flow';
import {ExtendedAuthorizeRequestUrlParams} from './models/oauth-request';
import {OIDCDiscoveryApiResponse} from './models/oidc-discovery';
import {OIDCEndpoints} from './models/oidc-endpoints';
Expand Down Expand Up @@ -317,15 +304,11 @@ class ThunderIDJavaScriptClient<T = Config> implements ThunderIDClient<T> {
throw new Error('Method not implemented.');
}

public signUp(options?: SignUpOptions): Promise<void>;
public signUp(payload: EmbeddedFlowExecuteRequestPayload): Promise<EmbeddedFlowExecuteResponse>;
public signUp(
_optionsOrPayload?: SignUpOptions | EmbeddedFlowExecuteRequestPayload,
): Promise<void | EmbeddedFlowExecuteResponse> {
public signUp(_options?: SignUpOptions): Promise<void> {
throw new Error('Method not implemented.');
}

public recover(_payload: EmbeddedFlowExecuteRequestPayload): Promise<EmbeddedFlowExecuteResponse> {
public recover(): Promise<void> {
throw new Error('Method not implemented.');
}

Expand Down Expand Up @@ -1001,7 +984,7 @@ class ThunderIDJavaScriptClient<T = Config> implements ThunderIDClient<T> {
}

if (pollResponse.ok) {
return this.authHelper.handleTokenResponse(pollResponse) as Promise<TokenResponse>;
return this.authHelper.handleTokenResponse(pollResponse);
}

const rawPollBody = await pollResponse.text().catch(() => '');
Expand Down Expand Up @@ -1073,52 +1056,6 @@ class ThunderIDJavaScriptClient<T = Config> implements ThunderIDClient<T> {

// ─── Agent / OBO helpers ─────────────────────────────────────────────────────

public async getAgentToken(agentConfig: AgentConfig): Promise<TokenResponse> {
const customParam: Record<string, string> = {response_mode: 'direct'};
const authorizeURL: URL = new URL(await this.getSignInUrl(customParam));

const authorizeResponse: EmbeddedSignInFlowInitiateResponse = await initializeEmbeddedSignInFlow({
payload: Object.fromEntries(authorizeURL.searchParams.entries()),
url: `${authorizeURL.origin}${authorizeURL.pathname}`,
});

const authenticatorName: string = agentConfig.authenticatorName ?? AgentConfig.DEFAULT_AUTHENTICATOR_NAME;
const targetAuthenticator: EmbeddedSignInFlowAuthenticator | undefined =
authorizeResponse.nextStep.authenticators.find(
(auth: EmbeddedSignInFlowAuthenticator) => auth.authenticator === authenticatorName,
);

if (!targetAuthenticator) {
throw new Error(`Authenticator '${authenticatorName}' not found among authentication steps.`);
}

const authnRequest: EmbeddedFlowExecuteRequestConfig = {
baseUrl: this.baseURL,
payload: {
flowId: authorizeResponse.flowId,
selectedAuthenticator: {
authenticatorId: targetAuthenticator.authenticatorId,
params: {
password: agentConfig.agentSecret,
username: agentConfig.agentID,
},
},
},
};

const authnResponse: EmbeddedSignInFlowHandleResponse = await executeEmbeddedSignInFlow(authnRequest);

if (authnResponse.flowStatus !== EmbeddedSignInFlowStatus.SuccessCompleted) {
throw new Error('Agent authentication failed.');
}

return this.requestAccessToken(
authnResponse.authData['code'],
authnResponse.authData['session_state'],
authnResponse.authData['state'],
);
}

public async getOBOSignInURL(agentConfig: AgentConfig): Promise<string> {
const authURL: string = await this.getSignInUrl({requested_actor: agentConfig.agentID});

Expand All @@ -1128,18 +1065,6 @@ class ThunderIDJavaScriptClient<T = Config> implements ThunderIDClient<T> {

throw new Error('Could not build Authorize URL');
}

public async getOBOToken(agentConfig: AgentConfig, authCodeResponse: AuthCodeResponse): Promise<TokenResponse> {
const agentToken: TokenResponse = await this.getAgentToken(agentConfig);

return this.requestAccessToken(
authCodeResponse.code,
authCodeResponse.session_state,
authCodeResponse.state,
undefined,
{params: {actor_token: agentToken.accessToken}},
);
}
}

export default ThunderIDJavaScriptClient;
26 changes: 13 additions & 13 deletions packages/javascript/src/__tests__/ThunderIDJavaScriptClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
*/

import {describe, expect, it, vi, beforeEach, afterEach} from 'vitest';
import ThunderIDJavaScriptClient from '../ThunderIDJavaScriptClient';
import type {Storage} from '../models/store';
import ThunderIDJavaScriptClient from '../ThunderIDJavaScriptClient';

vi.mock('../IsomorphicCrypto', () => ({
IsomorphicCrypto: class MockIsomorphicCrypto {
Expand Down Expand Up @@ -107,9 +107,9 @@ describe('ThunderIDJavaScriptClient', () => {

expect(config['enablePKCE']).toBe(true);
expect(config['sendCookiesInRequests']).toBe(true);
expect(config['tokenValidation']['idToken']['clockTolerance']).toBe(300);
expect(config['tokenValidation']['idToken']['validate']).toBe(true);
expect(config['tokenValidation']['idToken']['validateIssuer']).toBe(true);
expect(config['tokenValidation'].idToken.clockTolerance).toBe(300);
expect(config['tokenValidation'].idToken.validate).toBe(true);
expect(config['tokenValidation'].idToken.validateIssuer).toBe(true);
});

it('should deep-merge partial tokenValidation, preserving sibling defaults', async () => {
Expand All @@ -123,9 +123,9 @@ describe('ThunderIDJavaScriptClient', () => {

const config = await getStoredConfig(client);

expect(config['tokenValidation']['idToken']['validate']).toBe(false);
expect(config['tokenValidation']['idToken']['clockTolerance']).toBe(300);
expect(config['tokenValidation']['idToken']['validateIssuer']).toBe(true);
expect(config['tokenValidation'].idToken.validate).toBe(false);
expect(config['tokenValidation'].idToken.clockTolerance).toBe(300);
expect(config['tokenValidation'].idToken.validateIssuer).toBe(true);
});

it('should allow individual tokenValidation fields to be overridden independently', async () => {
Expand All @@ -139,9 +139,9 @@ describe('ThunderIDJavaScriptClient', () => {

const config = await getStoredConfig(client);

expect(config['tokenValidation']['idToken']['clockTolerance']).toBe(60);
expect(config['tokenValidation']['idToken']['validate']).toBe(true);
expect(config['tokenValidation']['idToken']['validateIssuer']).toBe(true);
expect(config['tokenValidation'].idToken.clockTolerance).toBe(60);
expect(config['tokenValidation'].idToken.validate).toBe(true);
expect(config['tokenValidation'].idToken.validateIssuer).toBe(true);
});

it('should set explicit fields (applicationId, scope) at highest precedence', async () => {
Expand Down Expand Up @@ -242,14 +242,14 @@ describe('ThunderIDJavaScriptClient', () => {
await client.initiateCIBA({bindingMessage: 'Approve login', loginHint: 'user@example.com'});

const [, init] = fetchMock.mock.calls[0];
expect(init.headers['Authorization']).toMatch(/^Basic /);
expect(init.headers.Authorization).toMatch(/^Basic /);
const body: URLSearchParams = init.body;
expect(body.has('client_secret')).toBe(false);
});

it('should send client_secret in body and no Authorization header when using client_secret_post', async () => {
const client = new ThunderIDJavaScriptClient(store, {} as any);
await client.initialize({...BASE_CONFIG, tokenRequest: {authMethod: 'client_secret_post'}} as any);
await client.initialize({...BASE_CONFIG, tokenRequest: {authMethod: 'client_secret_post'}});
const sm = (client as any).storageManager;
await sm.setOIDCProviderMetaData(OIDC_META);
await sm.setTemporaryDataParameter('op_config_initiated', true);
Expand All @@ -266,7 +266,7 @@ describe('ThunderIDJavaScriptClient', () => {
await client.initiateCIBA({loginHint: 'user@example.com'});

const [, init] = fetchMock.mock.calls[0];
expect(init.headers['Authorization']).toBeUndefined();
expect(init.headers.Authorization).toBeUndefined();
const body: URLSearchParams = init.body;
expect(body.get('client_secret')).toBe('test-secret');
});
Expand Down
Loading
Loading