Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3af510f
feat(core, react): update components to handle step up flows
NaveenChand755 Feb 17, 2026
f146f65
feat(react): update gatekeeper
NaveenChand755 Feb 17, 2026
30e8e20
Merge branch 'main' into feat/step-up-handler
NaveenChand755 Feb 17, 2026
6503083
feat(react): remove use error handler
NaveenChand755 Feb 17, 2026
0120bb4
feat(core, react): refactore to remove scope manager
NaveenChand755 Feb 17, 2026
6c638ac
fix(examples): revert testing changes
NaveenChand755 Feb 18, 2026
5c84f0f
fix(react): resolve conflicts
NaveenChand755 Feb 18, 2026
e259e3d
fix(react): reverted unwanted change
NaveenChand755 Feb 18, 2026
6c5e8f0
fix(core,react): resolve conflicts
NaveenChand755 Mar 2, 2026
2d7c839
fix(react): view and container split
NaveenChand755 Mar 2, 2026
bad7c98
fix(react): test cases update
NaveenChand755 Mar 2, 2026
4d01500
fix(react): add code coverage
NaveenChand755 Mar 2, 2026
262c27e
fix(react): add code cov for providers
NaveenChand755 Mar 2, 2026
a9b8016
feat(react): add mfa step-up authentication flow with enrollment support
harishsundar-okta Mar 3, 2026
d8afde1
fix(react): add missing jsdoc @param and @returns to step-up components
harishsundar-okta Mar 3, 2026
a6ee389
test(react): update gatekeeper mfa tests to match step-up output
harishsundar-okta Mar 3, 2026
f6a16be
fix(react): update list ui for step up
NaveenChand755 Mar 3, 2026
1614dd3
fix(react): update flow for recovery code
NaveenChand755 Mar 3, 2026
a992c20
feat(react): create scim tokens and provisioning hooks
NaveenChand755 Mar 4, 2026
55b8cc5
feat(react): update scim tokens and provisioning hooks
NaveenChand755 Mar 4, 2026
3349a82
feat(react): test case update
NaveenChand755 Mar 4, 2026
32b6647
fix(react,core): fix typos
NaveenChand755 Mar 4, 2026
7aa45cd
fix(react,core): test case update for use domain tab
NaveenChand755 Mar 4, 2026
90f8ea4
fix(react): fix step up forms ui
NaveenChand755 Mar 4, 2026
50ad919
Merge branch 'feat/step-up-handler' of https://github.com/auth0/auth0…
harishsundar-okta Mar 4, 2026
1775f3f
fix(react): fix enrollment flow ui
NaveenChand755 Mar 4, 2026
2140d88
Merge branch 'feat/mfa-list-challenge-verify-flow-UIC-573' of https:/…
harishsundar-okta Mar 4, 2026
88dbcf4
fix(core,react): refactor qr code form
NaveenChand755 Mar 6, 2026
2fbe351
fix(core,react): strings update
NaveenChand755 Mar 9, 2026
3175b2d
fix(react): resolve merge conflicts
NaveenChand755 Mar 9, 2026
f190b79
feat(react): add recovery code flow
NaveenChand755 Mar 9, 2026
9e9aa55
Merge pull request #100 from auth0/feat/mfa-list-challenge-verify-flo…
NaveenChand755 Mar 9, 2026
45e4661
fix(core,react): review comments update
NaveenChand755 Mar 9, 2026
38bac94
Merge pull request #124 from auth0/feat/mfa-list-challenge-verify-flo…
NaveenChand755 Mar 9, 2026
5003619
fix(react): resolve conflicts
NaveenChand755 Mar 10, 2026
f96aa37
fix(core): test case update
NaveenChand755 Mar 10, 2026
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
2 changes: 1 addition & 1 deletion docs-site/src/pages/SsoProviderEditDocs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,7 @@ interface ComponentAction<T, U = undefined> {
<li>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have designs for this? I think the UI experience is not aligned with the rest of the components.

Image
  • Texts must be aligned to the left, not centered
  • Buttons must be on the bottom and right side
  • Cancel button must be outlined

Some examples in other components:

Image Image

<code>tabs.provisioning.content.notifications.*</code> – Notification messages
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spacing between input and error is too big:

Image

(delete_success, remove_success, update_success, general_error,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Designs are not aligned:

  • Not sure about the cards, but probably we should use separators instead
  • Should Verify button be primary?

Modal:
Image

MFA component:
Image

provisioning_disabled_success, scim_token_delete_sucess)
provisioning_disabled_success, scim_token_delete_success)
</li>
</ul>
</div>
Expand Down
2 changes: 1 addition & 1 deletion examples/next-rwa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"lint": "next lint"
},
"dependencies": {
"@auth0/nextjs-auth0": "^4.13.2",
"@auth0/nextjs-auth0": "^v4.15.0",
"@auth0/universal-components-react": "workspace:*",
"@tailwindcss/postcss": "^4.1.17",
"execa": "^9.1.0",
Expand Down
1 change: 0 additions & 1 deletion examples/next-rwa/src/lib/auth0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export const auth0 = new Auth0Client({
httpTimeout: 20000, // 20 seconds
authorizationParameters: {
scope: process.env.AUTH0_SCOPE || 'openid profile email offline_access',

...(process.env.AUTH0_DOMAIN && {
audience: `${process.env.AUTH0_DOMAIN.replace(/\/$/, '')}/my-org/`,
}),
Expand Down
2 changes: 1 addition & 1 deletion examples/react-spa-npm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"@auth0/auth0-react": "^2.12.0",
"@auth0/auth0-react": "^2.15.0",
"@auth0/universal-components-react": "workspace:*",
"i18next": "^25.2.1",
"lucide-react": "^0.511.0",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@

export * from './api-error';
export * from './business-error';
export * from './proxy-http-client';
58 changes: 58 additions & 0 deletions packages/core/src/api/proxy-http-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Shared HTTP client for proxy-mode API calls.
* @module proxy-http-client
* @internal
*/

/**
* A lightweight HTTP client scoped to a base URL.
* Provides `get` and `post` helpers with standardized error handling.
*/
export interface ProxyHttpClient {
/** Sends a GET request with optional query parameters. */
get: <T>(path: string, query?: Record<string, string>) => Promise<T>;
/** Sends a POST request with a JSON body. */
post: <T>(path: string, body: unknown) => Promise<T>;
}

/**
* Creates a proxy HTTP client scoped to the given base URL.
*
* @param baseUrl - The base URL for all requests (trailing slash is stripped).
* @returns A {@link ProxyHttpClient} with `get` and `post` methods.
*/
export function createProxyHttpClient(baseUrl: string): ProxyHttpClient {
const normalizedBase = baseUrl.replace(/\/$/, '');

const handleResponse = async <T>(response: Response): Promise<T> => {
if (!response.ok) {
const errorBody = await response.json().catch(() => ({}));
throw Object.assign(new Error(errorBody.error_description || `HTTP ${response.status}`), {
status: response.status,
body: errorBody,
...errorBody,
});
}
return response.json();
};

const get = async <T>(path: string, query?: Record<string, string>): Promise<T> => {
const url = new URL(`${normalizedBase}${path}`);
if (query) {
Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v));
}
const response = await fetch(url.toString());
return handleResponse<T>(response);
};

const post = async <T>(path: string, body: unknown): Promise<T> => {
const response = await fetch(`${normalizedBase}${path}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
return handleResponse<T>(response);
};

return { get, post };
}
88 changes: 56 additions & 32 deletions packages/core/src/auth/__mocks__/core-client.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type {
BasicAuth0ContextInterface,
CoreClientInterface,
User,
Auth0ContextInterface,
GetTokenSilentlyVerboseResponse,
GetTokenSilentlyOptions,
} from '../auth-types';
Expand Down Expand Up @@ -39,6 +38,29 @@ export const createMockVerboseTokenResponse = (
...overrides,
});

/**
* Creates a mock MFA API client
*/
const createMockMfaClient = () => ({
getAuthenticators: vi.fn().mockResolvedValue([]),
enroll: vi.fn().mockResolvedValue({
authenticatorType: 'otp',
secret: 'mock-secret',
barcodeUri: 'otpauth://totp/mock',
id: 'authenticator_123',
}),
challenge: vi.fn().mockResolvedValue({
challengeType: 'oob',
oobCode: 'mock-oob-code',
}),
getEnrollmentFactors: vi.fn().mockResolvedValue([]),
verify: vi.fn().mockResolvedValue({
id_token: 'mock-id-token',
access_token: 'mock-access-token',
expires_in: 3600,
}),
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should separate everything related to MFA into different files (types, mocks, etc).
This is a new API that should not be related to coreClient

/**
* Creates a mock BasicAuth0ContextInterface
*/
Expand All @@ -59,36 +81,7 @@ export const createMockBasicAuth0Context = (
domain: TEST_DOMAIN,
clientId: TEST_CLIENT_ID,
}),
...overrides,
});

/**
* Creates a mock Auth0ContextInterface with full properties
*/
export const createMockAuth0Context = (
overrides?: Partial<Auth0ContextInterface>,
): Auth0ContextInterface => ({
isAuthenticated: true,
isLoading: false,
user: createMockUser(),
getAccessTokenSilently: vi.fn().mockImplementation(async (options?: GetTokenSilentlyOptions) => {
if (options?.detailedResponse) {
return createMockVerboseTokenResponse();
}
return 'mock-access-token';
}),
getAccessTokenWithPopup: vi.fn().mockResolvedValue('mock-access-token'),
loginWithRedirect: vi.fn().mockResolvedValue(undefined),
loginWithPopup: vi.fn().mockResolvedValue(undefined),
logout: vi.fn().mockResolvedValue(undefined),
getIdTokenClaims: vi.fn().mockResolvedValue({
sub: 'auth0|test-user-123',
aud: 'test-client-id',
iss: 'https://test-domain.auth0.com/',
}),
handleRedirectCallback: vi.fn().mockResolvedValue({
appState: {},
}),
mfa: createMockMfaClient(),
...overrides,
});

Expand All @@ -115,9 +108,35 @@ export const createMockMyAccountApiClient = (): CoreClientInterface['myAccountAp
delete: vi.fn().mockResolvedValue(undefined),
verify: vi.fn().mockResolvedValue({ confirmed: true }),
},
withScopes: vi.fn().mockReturnThis(),
} as unknown as CoreClientInterface['myAccountApiClient'];
};

/**
* Creates a mock StepUpApiService
*/
export const createMockStepUpApiService = (): CoreClientInterface['stepUpApiService'] => {
return {
getAuthenticators: vi.fn().mockResolvedValue([]),
enroll: vi.fn().mockResolvedValue({
authenticatorType: 'otp',
secret: 'mock-secret',
barcodeUri: 'otpauth://totp/mock',
id: 'authenticator_123',
}),
challenge: vi.fn().mockResolvedValue({
challengeType: 'oob',
oobCode: 'mock-oob-code',
}),
getEnrollmentFactors: vi.fn().mockResolvedValue([]),
verify: vi.fn().mockResolvedValue({
id_token: 'mock-id-token',
access_token: 'mock-access-token',
expires_in: 3600,
}),
} as unknown as CoreClientInterface['stepUpApiService'];
};

/**
* Creates a mock MyOrganizationClient service
*/
Expand Down Expand Up @@ -203,6 +222,7 @@ export const createMockMyOrganizationApiClient =
},
},
},
withScopes: vi.fn().mockReturnThis(),
} as unknown as CoreClientInterface['myOrganizationApiClient'];
};

Expand All @@ -214,21 +234,25 @@ export const createMockCoreClient = (authDetails?: Partial<AuthDetails>): CoreCl
const mockI18nService = createMockI18nService();
const mockMyAccountApiClient = createMockMyAccountApiClient();
const mockMyOrganizationApiClient = createMockMyOrganizationApiClient();
const mockStepUpApiService = createMockStepUpApiService();

return {
auth: mockAuth,
i18nService: mockI18nService,
myAccountApiClient: mockMyAccountApiClient,
myOrganizationApiClient: mockMyOrganizationApiClient,
stepUpApiService: mockStepUpApiService,
getMyAccountApiClient: vi.fn(
() => mockMyAccountApiClient,
) as CoreClientInterface['getMyAccountApiClient'],
getMyOrganizationApiClient: vi.fn(
() => mockMyOrganizationApiClient,
) as CoreClientInterface['getMyOrganizationApiClient'],
getStepUpApiService: vi.fn(
() => mockStepUpApiService,
) as CoreClientInterface['getStepUpApiService'],
getToken: vi.fn().mockResolvedValue('mock-access-token'),
isProxyMode: vi.fn().mockReturnValue(false),
ensureScopes: vi.fn().mockResolvedValue(undefined),
getDomain: vi.fn(
() => mockAuth.domain ?? mockAuth.contextInterface?.getConfiguration()?.domain,
),
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/auth/__mocks__/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './core-client.mocks';
export * from './token-manager.mocks';
export * from './spa-token-retriever.mocks';
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { vi } from 'vitest';

import type { createTokenManager } from '../token-manager';
import type { createSpaTokenRetriever } from '../spa-token-retriever';

/**
* Creates a mock token manager service
* Creates a mock SPA token retriever service
*/
export const createMockTokenManager = (
export const createMockSpaTokenRetriever = (
tokenValue: string | undefined = 'mock-access-token',
): ReturnType<typeof createTokenManager> => ({
): ReturnType<typeof createSpaTokenRetriever> => ({
getToken: vi.fn(async () => tokenValue),
});

export const createMockTokenManagerWithScopes = (
export const createMockSpaTokenRetrieverWithScopes = (
tokenValue: string | undefined = 'mock-access-token',
): ReturnType<typeof createTokenManager> & {
): ReturnType<typeof createSpaTokenRetriever> & {
lastScope?: string;
lastAudiencePath?: string;
} => {
Expand All @@ -29,9 +29,9 @@ export const createMockTokenManagerWithScopes = (
return mockManager;
};

export const createMockTokenManagerWithError = (
export const createMockSpaTokenRetrieverWithError = (
error: Error = new Error('Token retrieval failed'),
): ReturnType<typeof createTokenManager> => ({
): ReturnType<typeof createSpaTokenRetriever> => ({
getToken: async () => {
throw error;
},
Expand Down
Loading
Loading