0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-04-14 23:11:31 -05:00

chore: prepare to release the one-time token feature

This commit is contained in:
Charles Zhao 2025-04-08 11:05:53 +08:00
parent ae01f34c50
commit 2f9b86a318
No known key found for this signature in database
GPG key ID: 55CFA7C080C98029
12 changed files with 20 additions and 40 deletions

View file

@ -0,0 +1,7 @@
---
"@logto/integration-tests": minor
"@logto/experience": minor
"@logto/core": minor
---
feat: add one-time token verification method to support magic link authentication

View file

@ -197,9 +197,7 @@ export default function experienceApiRoutes<T extends AnonymousRouter>(
webAuthnVerificationRoute(experienceRouter, tenant);
backupCodeVerificationRoutes(experienceRouter, tenant);
newPasswordIdentityVerificationRoutes(experienceRouter, tenant);
if (EnvSet.values.isDevFeaturesEnabled) {
oneTimeTokenRoutes(experienceRouter, tenant);
}
oneTimeTokenRoutes(experienceRouter, tenant);
profileRoutes(experienceRouter, tenant);
experienceAnonymousRoutes(experienceRouter, tenant);

View file

@ -1,9 +1,4 @@
{
"tags": [
{
"name": "Dev feature"
}
],
"paths": {
"/api/experience/verification/one-time-token/verify": {
"post": {

View file

@ -106,8 +106,8 @@ const createRouters = (tenant: TenantContext) => {
accountCentersRoutes(managementRouter, tenant);
samlApplicationRoutes(managementRouter, tenant);
emailTemplateRoutes(managementRouter, tenant);
oneTimeTokenRoutes(managementRouter, tenant);
if (EnvSet.values.isDevFeaturesEnabled) {
oneTimeTokenRoutes(managementRouter, tenant);
captchaProviderRoutes(managementRouter, tenant);
}

View file

@ -3,9 +3,6 @@
{
"name": "One-time tokens",
"description": "One-time tokens are used for various authentication and verification purposes. They are typically sent via email and have an expiration time."
},
{
"name": "Dev feature"
}
],
"paths": {

View file

@ -62,7 +62,7 @@ const additionalTags = Object.freeze(
'Organization users',
'SAML applications',
'SAML applications auth flow',
EnvSet.values.isDevFeaturesEnabled && 'One-time tokens',
'One-time tokens',
EnvSet.values.isDevFeaturesEnabled && 'Captcha provider'
)
);

View file

@ -37,12 +37,9 @@ const tagMap = new Map([
['.well-known', 'Well-known'],
['saml-applications', 'SAML applications'],
['saml', 'SAML applications auth flow'],
['one-time-tokens', 'One-time tokens'],
]);
if (EnvSet.values.isDevFeaturesEnabled) {
tagMap.set('one-time-tokens', 'One-time tokens');
}
/**
* Build a tag name from the given absolute path. The function will get the root component name
* from the path and try to find the mapping in the {@link tagMap}. If the mapping is not found,

View file

@ -27,10 +27,7 @@ const methodToVerb = Object.freeze({
type RouteDictionary = Record<`${OpenAPIV3.HttpMethods} ${string}`, string>;
const devFeatureCustomRoutes: RouteDictionary = Object.freeze({
'post /one-time-tokens': 'AddOneTimeTokens',
'post /one-time-tokens/verify': 'VerifyOneTimeToken',
});
const devFeatureCustomRoutes: RouteDictionary = Object.freeze({});
export const customRoutes: Readonly<RouteDictionary> = Object.freeze({
// Authn
@ -86,6 +83,9 @@ export const customRoutes: Readonly<RouteDictionary> = Object.freeze({
'get /.well-known/sign-in-exp': 'GetSignInExperienceConfig',
// Custom UI assets
'post /sign-in-exp/default/custom-ui-assets': 'UploadCustomUiAssets',
// One-time tokens
'post /one-time-tokens': 'AddOneTimeTokens',
'post /one-time-tokens/verify': 'VerifyOneTimeToken',
...(EnvSet.values.isDevFeaturesEnabled ? devFeatureCustomRoutes : {}),
} satisfies RouteDictionary); // Key assertion doesn't work without `satisfies`

View file

@ -8,7 +8,6 @@ import LoadingLayerProvider from './Providers/LoadingLayerProvider';
import PageContextProvider from './Providers/PageContextProvider';
import SettingsProvider from './Providers/SettingsProvider';
import UserInteractionContextProvider from './Providers/UserInteractionContextProvider';
import { isDevFeaturesEnabled } from './constants/env';
import DevelopmentTenantNotification from './containers/DevelopmentTenantNotification';
import Callback from './pages/Callback';
import Consent from './pages/Consent';
@ -67,15 +66,8 @@ const App = () => {
/>
<Route path="direct/:method/:target?" element={<DirectSignIn />} />
<Route element={<AppLayout />}>
{isDevFeaturesEnabled && (
<>
<Route path={experience.routes.oneTimeToken} element={<OneTimeToken />} />
<Route
path={experience.routes.switchAccount}
element={<SwitchAccount />}
/>
</>
)}
<Route path={experience.routes.oneTimeToken} element={<OneTimeToken />} />
<Route path={experience.routes.switchAccount} element={<SwitchAccount />} />
<Route
path="unknown-session"
element={<ErrorPage message="error.invalid_session" />}

View file

@ -6,9 +6,7 @@ import { initExperienceClient, logoutClient, processSession } from '#src/helpers
import { setEmailConnector } from '#src/helpers/connector.js';
import { expectRejects } from '#src/helpers/index.js';
import { generateNewUser } from '#src/helpers/user.js';
import { generatePassword, devFeatureTest } from '#src/utils.js';
const { it, describe } = devFeatureTest;
import { generatePassword } from '#src/utils.js';
describe('Register interaction with one-time token happy path', () => {
beforeAll(async () => {

View file

@ -3,9 +3,7 @@ import { SignInIdentifier } from '@logto/schemas';
import { createOneTimeToken } from '#src/api/one-time-token.js';
import { initExperienceClient } from '#src/helpers/client.js';
import { expectRejects } from '#src/helpers/index.js';
import { devFeatureTest, waitFor } from '#src/utils.js';
const { it, describe } = devFeatureTest;
import { waitFor } from '#src/utils.js';
describe('One-time token verification APIs', () => {
it('should successfully verify one-time token', async () => {

View file

@ -11,9 +11,7 @@ import {
getOneTimeTokens,
} from '#src/api/one-time-token.js';
import { expectRejects } from '#src/helpers/index.js';
import { devFeatureTest, waitFor } from '#src/utils.js';
const { it, describe } = devFeatureTest;
import { waitFor } from '#src/utils.js';
describe('one-time tokens API', () => {
it('should create one-time token with default 10 mins expiration time', async () => {