mirror of
https://github.com/logto-io/logto.git
synced 2025-02-17 22:04:19 -05:00
feat(core,schemas,phrases): add application sign-in-experience apis (#5129)
add application sign-in-experience apis
This commit is contained in:
parent
541ea2919c
commit
6e82e99725
31 changed files with 368 additions and 35 deletions
|
@ -60,10 +60,7 @@ export const createApplicationLibrary = (queries: Queries) => {
|
|||
const validateThirdPartyApplicationById = async (applicationId: string) => {
|
||||
const application = await findApplicationById(applicationId);
|
||||
|
||||
assertThat(
|
||||
application.isThirdParty,
|
||||
'application.user_consent_scopes_only_for_third_party_applications'
|
||||
);
|
||||
assertThat(application.isThirdParty, 'application.third_party_application_only');
|
||||
};
|
||||
|
||||
// Guard that all scopes exist
|
||||
|
|
37
packages/core/src/queries/application-sign-in-experience.ts
Normal file
37
packages/core/src/queries/application-sign-in-experience.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { ApplicationSignInExperiences, type ApplicationSignInExperience } from '@logto/schemas';
|
||||
import { convertToIdentifiers } from '@logto/shared';
|
||||
import { sql, type CommonQueryMethods } from 'slonik';
|
||||
|
||||
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
|
||||
import { buildUpdateWhereWithPool } from '#src/database/update-where.js';
|
||||
|
||||
const createApplicationSignInExperienceQueries = (pool: CommonQueryMethods) => {
|
||||
const insert = buildInsertIntoWithPool(pool)(ApplicationSignInExperiences, {
|
||||
returning: true,
|
||||
});
|
||||
|
||||
const safeFindSignInExperienceByApplicationId = async (applicationId: string) => {
|
||||
const { table, fields } = convertToIdentifiers(ApplicationSignInExperiences);
|
||||
|
||||
return pool.maybeOne<ApplicationSignInExperience>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.applicationId}=${applicationId}
|
||||
`);
|
||||
};
|
||||
|
||||
const update = buildUpdateWhereWithPool(pool)(ApplicationSignInExperiences, true);
|
||||
|
||||
const updateByApplicationId = async (
|
||||
applicationId: string,
|
||||
set: Partial<Omit<ApplicationSignInExperience, 'applicationId' | 'tenantId'>>
|
||||
) => update({ set, where: { applicationId }, jsonbMode: 'replace' });
|
||||
|
||||
return {
|
||||
insert,
|
||||
safeFindSignInExperienceByApplicationId,
|
||||
updateByApplicationId,
|
||||
};
|
||||
};
|
||||
|
||||
export default createApplicationSignInExperienceQueries;
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"paths": {
|
||||
"/api/applications/{applicationId}/sign-in-experience": {
|
||||
"put": {
|
||||
"summary": "Update application level sign-in experience",
|
||||
"description": "Update application level sign-in experience for the specified application. Create a new sign-in experience if it does not exist. \n - Only branding properties and terms links customization is supported for now. \n\n - Only third-party applications can be customized for now. \n\n - Application level sign-in experience customization is optional, if provided, it will override the default branding and terms links.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The application's sign-in experience was successfully updated."
|
||||
},
|
||||
"201": {
|
||||
"description": "A new application level sign-in experience settings was successfully created."
|
||||
},
|
||||
"404": {
|
||||
"description": "The application does not exist."
|
||||
}
|
||||
}
|
||||
},
|
||||
"get": {
|
||||
"summary": "Get the application level sign-in experience",
|
||||
"description": "Get application level sign-in experience for a given application. \n - Only branding properties and terms links customization is supported for now. \n\n - Only third-party applications can have the sign-in experience customization for now.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Returns the application's application level sign-in experience."
|
||||
},
|
||||
"404": {
|
||||
"description": "The application does not exist or the application level sign-in experience does not exist."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
import {
|
||||
ApplicationSignInExperiences,
|
||||
applicationSignInExperienceCreateGuard,
|
||||
} from '@logto/schemas';
|
||||
import { object, string } from 'zod';
|
||||
|
||||
import RequestError from '#src/errors/RequestError/index.js';
|
||||
import koaGuard from '#src/middleware/koa-guard.js';
|
||||
|
||||
import type { AuthedRouter, RouterInitArgs } from '../types.js';
|
||||
|
||||
function applicationSignInExperienceRoutes<T extends AuthedRouter>(
|
||||
...[
|
||||
router,
|
||||
{
|
||||
queries: {
|
||||
applications: { findApplicationById },
|
||||
applicationSignInExperiences: {
|
||||
safeFindSignInExperienceByApplicationId,
|
||||
insert,
|
||||
updateByApplicationId,
|
||||
},
|
||||
},
|
||||
libraries: {
|
||||
applications: { validateThirdPartyApplicationById },
|
||||
},
|
||||
},
|
||||
]: RouterInitArgs<T>
|
||||
) {
|
||||
/**
|
||||
* Customize the branding of an application.
|
||||
*
|
||||
* - Only branding and terms links customization is supported for now. e.g. per app level sign-in method customization is not supported.
|
||||
* - Only third-party applications can be customized for now.
|
||||
* - Application level sign-in experience customization is optional, if provided, it will override the default branding and terms links.
|
||||
* - We use application ID as the unique identifier of the application level sign-in experience ID.
|
||||
*/
|
||||
router.put(
|
||||
'/applications/:applicationId/sign-in-experience',
|
||||
koaGuard({
|
||||
params: object({
|
||||
applicationId: string(),
|
||||
}),
|
||||
body: applicationSignInExperienceCreateGuard,
|
||||
response: ApplicationSignInExperiences.guard,
|
||||
status: [200, 201, 404],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
params: { applicationId },
|
||||
body,
|
||||
} = ctx.guard;
|
||||
|
||||
await validateThirdPartyApplicationById(applicationId);
|
||||
|
||||
const applicationSignInExperience = await safeFindSignInExperienceByApplicationId(
|
||||
applicationId
|
||||
);
|
||||
|
||||
if (applicationSignInExperience) {
|
||||
const updatedApplicationSignInExperience = await updateByApplicationId(applicationId, body);
|
||||
|
||||
ctx.body = updatedApplicationSignInExperience;
|
||||
ctx.status = 200;
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
const newApplicationSignInExperience = await insert({
|
||||
...body,
|
||||
applicationId,
|
||||
});
|
||||
|
||||
ctx.body = newApplicationSignInExperience;
|
||||
|
||||
ctx.status = 201;
|
||||
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/applications/:applicationId/sign-in-experience',
|
||||
koaGuard({
|
||||
params: object({
|
||||
applicationId: string(),
|
||||
}),
|
||||
response: ApplicationSignInExperiences.guard,
|
||||
status: [200, 404],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
params: { applicationId },
|
||||
} = ctx.guard;
|
||||
|
||||
await findApplicationById(applicationId);
|
||||
|
||||
const applicationSignInExperience = await safeFindSignInExperienceByApplicationId(
|
||||
applicationId
|
||||
);
|
||||
|
||||
if (!applicationSignInExperience) {
|
||||
throw new RequestError({
|
||||
code: 'entity.not_exists_with_id',
|
||||
name: ApplicationSignInExperiences.table,
|
||||
id: applicationId,
|
||||
status: 404,
|
||||
});
|
||||
}
|
||||
|
||||
ctx.body = applicationSignInExperience;
|
||||
|
||||
ctx.status = 200;
|
||||
|
||||
return next();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default applicationSignInExperienceRoutes;
|
|
@ -12,6 +12,7 @@ import koaAuth from '../middleware/koa-auth/index.js';
|
|||
|
||||
import adminUserRoutes from './admin-user/index.js';
|
||||
import applicationRoleRoutes from './applications/application-role.js';
|
||||
import applicationSignInExperienceRoutes from './applications/application-sign-in-experience.js';
|
||||
import applicationUserConsentScopeRoutes from './applications/application-user-consent-scope.js';
|
||||
import applicationRoutes from './applications/application.js';
|
||||
import authnRoutes from './authn.js';
|
||||
|
@ -49,6 +50,7 @@ const createRouters = (tenant: TenantContext) => {
|
|||
|
||||
if (EnvSet.values.isDevFeaturesEnabled) {
|
||||
applicationUserConsentScopeRoutes(managementRouter, tenant);
|
||||
applicationSignInExperienceRoutes(managementRouter, tenant);
|
||||
}
|
||||
|
||||
logtoConfigRoutes(managementRouter, tenant);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { CommonQueryMethods } from 'slonik';
|
||||
|
||||
import { type WellKnownCache } from '#src/caches/well-known.js';
|
||||
import createApplicationSignInExperienceQueries from '#src/queries/application-sign-in-experience.js';
|
||||
import { createApplicationQueries } from '#src/queries/application.js';
|
||||
import { createApplicationsRolesQueries } from '#src/queries/applications-roles.js';
|
||||
import { createConnectorQueries } from '#src/queries/connector.js';
|
||||
|
@ -26,6 +27,7 @@ import { createVerificationStatusQueries } from '#src/queries/verification-statu
|
|||
|
||||
export default class Queries {
|
||||
applications = createApplicationQueries(this.pool);
|
||||
applicationSignInExperiences = createApplicationSignInExperienceQueries(this.pool);
|
||||
connectors = createConnectorQueries(this.pool, this.wellKnownCache);
|
||||
customPhrases = createCustomPhraseQueries(this.pool, this.wellKnownCache);
|
||||
logs = createLogQueries(this.pool);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import {
|
||||
type ApplicationSignInExperienceCreate,
|
||||
type ApplicationSignInExperience,
|
||||
} from '@logto/schemas';
|
||||
|
||||
import { authedAdminApi } from './api.js';
|
||||
|
||||
export const setApplicationSignInExperience = async (
|
||||
applicationId: string,
|
||||
payload: ApplicationSignInExperienceCreate
|
||||
) =>
|
||||
authedAdminApi
|
||||
.put(`applications/${applicationId}/sign-in-experience`, { json: payload })
|
||||
.json<ApplicationSignInExperience>();
|
||||
|
||||
export const getApplicationSignInExperience = async (applicationId: string) =>
|
||||
authedAdminApi
|
||||
.get(`applications/${applicationId}/sign-in-experience`)
|
||||
.json<ApplicationSignInExperience>();
|
|
@ -0,0 +1,118 @@
|
|||
import {
|
||||
ApplicationType,
|
||||
type ApplicationSignInExperienceCreate,
|
||||
type Application,
|
||||
} from '@logto/schemas';
|
||||
|
||||
import {
|
||||
getApplicationSignInExperience,
|
||||
setApplicationSignInExperience,
|
||||
} from '#src/api/application-sign-in-experience.js';
|
||||
import { createApplication, deleteApplication } from '#src/api/application.js';
|
||||
import { expectRejects } from '#src/helpers/index.js';
|
||||
|
||||
describe('application sign in experience', () => {
|
||||
const applications = new Map<string, Application>();
|
||||
|
||||
const applicationSignInExperiences: ApplicationSignInExperienceCreate = {
|
||||
branding: {
|
||||
logoUrl: 'https://logto.dev/logo.png',
|
||||
darkLogoUrl: 'https://logto.dev/logo-dark.png',
|
||||
},
|
||||
termsOfUseUrl: 'https://logto.dev/terms-of-use',
|
||||
privacyPolicyUrl: 'https://logto.dev/privacy-policy',
|
||||
displayName: 'Logto Demo',
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const firstPartyApp = await createApplication('first-party-application', ApplicationType.SPA);
|
||||
const thirdPartyApp = await createApplication(
|
||||
'third-party-application',
|
||||
ApplicationType.Traditional,
|
||||
{
|
||||
isThirdParty: true,
|
||||
}
|
||||
);
|
||||
|
||||
applications.set('firstPartyApp', firstPartyApp);
|
||||
applications.set('thirdPartyApp', thirdPartyApp);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await Promise.all(
|
||||
Array.from(applications.values()).map(async (applications) =>
|
||||
deleteApplication(applications.id)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw 404 if application does not exist', async () => {
|
||||
await expectRejects(
|
||||
setApplicationSignInExperience('non-existent-application', applicationSignInExperiences),
|
||||
{
|
||||
code: 'entity.not_exists_with_id',
|
||||
statusCode: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw 400 if application is not third-party', async () => {
|
||||
await expectRejects(
|
||||
setApplicationSignInExperience(
|
||||
applications.get('firstPartyApp')!.id,
|
||||
applicationSignInExperiences
|
||||
),
|
||||
{
|
||||
code: 'application.third_party_application_only',
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should set new application sign in experience', async () => {
|
||||
const application = applications.get('thirdPartyApp')!;
|
||||
|
||||
const signInExperience = await setApplicationSignInExperience(
|
||||
application.id,
|
||||
applicationSignInExperiences
|
||||
);
|
||||
|
||||
expect(signInExperience).toMatchObject({
|
||||
...applicationSignInExperiences,
|
||||
applicationId: application.id,
|
||||
tenantId: application.tenantId,
|
||||
});
|
||||
|
||||
const getSignInExperience = await getApplicationSignInExperience(application.id);
|
||||
expect(getSignInExperience).toMatchObject(signInExperience);
|
||||
});
|
||||
|
||||
it('should update existing application sign in experience', async () => {
|
||||
const application = applications.get('thirdPartyApp')!;
|
||||
|
||||
const signInExperience = await setApplicationSignInExperience(application.id, {
|
||||
...applicationSignInExperiences,
|
||||
displayName: '',
|
||||
});
|
||||
|
||||
expect(signInExperience).toMatchObject({
|
||||
...applicationSignInExperiences,
|
||||
displayName: null,
|
||||
applicationId: application.id,
|
||||
tenantId: application.tenantId,
|
||||
});
|
||||
|
||||
const getSignInExperience = await getApplicationSignInExperience(application.id);
|
||||
|
||||
expect(getSignInExperience).toMatchObject(signInExperience);
|
||||
});
|
||||
|
||||
it('should throw 404 if application sign in experience does not exist', async () => {
|
||||
const application = applications.get('firstPartyApp')!;
|
||||
|
||||
await expectRejects(getApplicationSignInExperience(application.id), {
|
||||
code: 'entity.not_exists_with_id',
|
||||
statusCode: 404,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -77,7 +77,7 @@ describe('assign user consent scopes to application', () => {
|
|||
resourceScopes: Array.from(resourceScopes.values()),
|
||||
}),
|
||||
{
|
||||
code: 'application.user_consent_scopes_only_for_third_party_applications',
|
||||
code: 'application.third_party_application_only',
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
|
@ -7,8 +7,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -4,8 +4,7 @@ const application = {
|
|||
invalid_role_type: 'Can not assign user type role to machine to machine application.',
|
||||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -7,8 +7,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -7,8 +7,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -7,8 +7,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -6,8 +6,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -6,8 +6,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -7,8 +7,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -7,8 +7,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -7,8 +7,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -6,8 +6,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -6,8 +6,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -6,8 +6,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -6,8 +6,7 @@ const application = {
|
|||
invalid_third_party_application_type:
|
||||
'Only traditional web applications can be marked as a third-party app.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_only_for_third_party_applications:
|
||||
'Only third-party applications can manage user consent scopes.',
|
||||
third_party_application_only: 'The feature is only available for third-party applications.',
|
||||
/** UNTRANSLATED */
|
||||
user_consent_scopes_not_found: 'Invalid user consent scopes.',
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
OrganizationScopes,
|
||||
Resources,
|
||||
Scopes,
|
||||
ApplicationSignInExperiences,
|
||||
} from '../db-entries/index.js';
|
||||
|
||||
export type ApplicationResponse = Application & { isAdmin: boolean };
|
||||
|
@ -61,3 +62,22 @@ export enum ApplicationUserConsentScopeType {
|
|||
export type ApplicationUserConsentScopesResponse = z.infer<
|
||||
typeof applicationUserConsentScopesResponseGuard
|
||||
>;
|
||||
|
||||
export const applicationSignInExperienceCreateGuard = ApplicationSignInExperiences.createGuard
|
||||
.omit({
|
||||
applicationId: true,
|
||||
tenantId: true,
|
||||
termsOfUseUrl: true,
|
||||
privacyPolicyUrl: true,
|
||||
})
|
||||
// Align with the sign-in-experience create guard.
|
||||
.merge(
|
||||
z.object({
|
||||
termsOfUseUrl: z.string().max(2048).url().optional().nullable().or(z.literal('')),
|
||||
privacyPolicyUrl: z.string().max(2048).url().optional().nullable().or(z.literal('')),
|
||||
})
|
||||
);
|
||||
|
||||
export type ApplicationSignInExperienceCreate = z.infer<
|
||||
typeof applicationSignInExperienceCreateGuard
|
||||
>;
|
||||
|
|
Loading…
Add table
Reference in a new issue