0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-27 21:39:16 -05:00

feat(core): sync site configs and oidc metadata for custom domain (#5260)

This commit is contained in:
wangsijie 2024-01-23 13:14:03 +08:00 committed by GitHub
parent 43ce51ce2a
commit fa89d33252
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 83 additions and 12 deletions

View file

@ -109,6 +109,33 @@ describe('syncAppConfigsToRemote()', () => {
}
);
});
it('should sync custom domains configs to remote', async () => {
findApplicationById.mockResolvedValueOnce({
...mockProtectedApplication,
protectedAppMetadata: {
...mockProtectedApplication.protectedAppMetadata,
customDomains: [mockCustomDomain],
},
});
await expect(syncAppConfigsToRemote(mockProtectedApplication.id)).resolves.not.toThrow();
const { protectedAppMetadata, id, secret } = mockProtectedApplication;
expect(updateProtectedAppSiteConfigs).toHaveBeenLastCalledWith(
protectedAppConfigProviderConfig,
mockCustomDomain.domain,
{
...protectedAppMetadata,
host: mockCustomDomain.domain,
sdkConfig: {
appId: id,
appSecret: secret,
// Avoid mocking envset
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
endpoint: expect.anything(),
},
}
);
});
});
describe('checkAndBuildProtectedAppData()', () => {

View file

@ -113,18 +113,35 @@ export const createProtectedAppLibrary = (queries: Queries) => {
return;
}
const { customDomains, ...rest } = protectedAppMetadata;
const siteConfigs = {
...rest,
sdkConfig: {
appId: id,
appSecret: secret,
endpoint: EnvSet.values.endpoint.href,
},
};
// Update default host (subdomain of the default domain)
await updateProtectedAppSiteConfigs(
protectedAppConfigProviderConfig,
protectedAppMetadata.host,
{
...protectedAppMetadata,
sdkConfig: {
appId: id,
appSecret: secret,
endpoint: EnvSet.values.endpoint.href,
},
}
siteConfigs
);
// Update custom domains sites
if (customDomains && customDomains.length > 0) {
await Promise.all(
customDomains.map(async ({ domain }) => {
await updateProtectedAppSiteConfigs(protectedAppConfigProviderConfig, domain, {
...siteConfigs,
host: domain,
});
})
);
}
};
/**

View file

@ -37,6 +37,7 @@ const syncAppCustomDomainStatus = jest.fn(async () => ({
customDomains: [mockDomainResponse],
},
}));
const syncAppConfigsToRemote = jest.fn();
await mockIdGenerators();
@ -51,7 +52,7 @@ const tenantContext = new MockTenant(
},
undefined,
{
protectedApps: { addDomainToRemote, syncAppCustomDomainStatus },
protectedApps: { addDomainToRemote, syncAppCustomDomainStatus, syncAppConfigsToRemote },
applications: { validateProtectedApplicationById: jest.fn() },
}
);
@ -78,7 +79,7 @@ describe('application protected app metadata routes', () => {
});
describe('POST /applications/:applicationId/protected-app-metadata/custom-domains', () => {
it('should return 201', async () => {
it('should return 201 and update OIDC metadata and sync site configs', async () => {
const response = await requester
.post(`/applications/${mockProtectedApplication.id}/protected-app-metadata/custom-domains`)
.send({
@ -90,7 +91,18 @@ describe('application protected app metadata routes', () => {
...mockProtectedApplication.protectedAppMetadata,
customDomains: [mockDomainResponse],
},
oidcClientMetadata: {
postLogoutRedirectUris: [
`https://${mockProtectedApplication.protectedAppMetadata.host}`,
`https://${mockDomain}`,
],
redirectUris: [
`https://${mockProtectedApplication.protectedAppMetadata.host}/callback`,
`https://${mockDomain}/callback`,
],
},
});
expect(syncAppConfigsToRemote).toHaveBeenCalledWith(mockProtectedApplication.id);
});
it('throw when domain exists', async () => {

View file

@ -20,7 +20,7 @@ export default function applicationProtectedAppMetadataRoutes<T extends AuthedRo
},
libraries: {
applications: { validateProtectedApplicationById },
protectedApps: { addDomainToRemote, syncAppCustomDomainStatus },
protectedApps: { addDomainToRemote, syncAppCustomDomainStatus, syncAppConfigsToRemote },
},
},
]: RouterInitArgs<T>
@ -68,7 +68,7 @@ export default function applicationProtectedAppMetadataRoutes<T extends AuthedRo
const { id } = ctx.guard.params;
const { domain } = ctx.guard.body;
const { protectedAppMetadata } = await findApplicationById(id);
const { protectedAppMetadata, oidcClientMetadata } = await findApplicationById(id);
assertThat(protectedAppMetadata, 'application.protected_app_not_configured');
assertThat(
@ -87,8 +87,23 @@ export default function applicationProtectedAppMetadataRoutes<T extends AuthedRo
const customDomain = await addDomainToRemote(domain);
await updateApplicationById(id, {
protectedAppMetadata: { ...protectedAppMetadata, customDomains: [customDomain] },
oidcClientMetadata: {
redirectUris: [...oidcClientMetadata.redirectUris, `https://${domain}/callback`],
postLogoutRedirectUris: [
...oidcClientMetadata.postLogoutRedirectUris,
`https://${domain}`,
],
},
});
try {
await syncAppConfigsToRemote(id);
} catch (error: unknown) {
// Revert changes
await updateApplicationById(id, { protectedAppMetadata, oidcClientMetadata });
throw error;
}
ctx.status = 201;
return next();
}