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()', () => { describe('checkAndBuildProtectedAppData()', () => {

View file

@ -113,18 +113,35 @@ export const createProtectedAppLibrary = (queries: Queries) => {
return; 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( await updateProtectedAppSiteConfigs(
protectedAppConfigProviderConfig, protectedAppConfigProviderConfig,
protectedAppMetadata.host, protectedAppMetadata.host,
{ siteConfigs
...protectedAppMetadata,
sdkConfig: {
appId: id,
appSecret: secret,
endpoint: EnvSet.values.endpoint.href,
},
}
); );
// 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], customDomains: [mockDomainResponse],
}, },
})); }));
const syncAppConfigsToRemote = jest.fn();
await mockIdGenerators(); await mockIdGenerators();
@ -51,7 +52,7 @@ const tenantContext = new MockTenant(
}, },
undefined, undefined,
{ {
protectedApps: { addDomainToRemote, syncAppCustomDomainStatus }, protectedApps: { addDomainToRemote, syncAppCustomDomainStatus, syncAppConfigsToRemote },
applications: { validateProtectedApplicationById: jest.fn() }, applications: { validateProtectedApplicationById: jest.fn() },
} }
); );
@ -78,7 +79,7 @@ describe('application protected app metadata routes', () => {
}); });
describe('POST /applications/:applicationId/protected-app-metadata/custom-domains', () => { 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 const response = await requester
.post(`/applications/${mockProtectedApplication.id}/protected-app-metadata/custom-domains`) .post(`/applications/${mockProtectedApplication.id}/protected-app-metadata/custom-domains`)
.send({ .send({
@ -90,7 +91,18 @@ describe('application protected app metadata routes', () => {
...mockProtectedApplication.protectedAppMetadata, ...mockProtectedApplication.protectedAppMetadata,
customDomains: [mockDomainResponse], 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 () => { it('throw when domain exists', async () => {

View file

@ -20,7 +20,7 @@ export default function applicationProtectedAppMetadataRoutes<T extends AuthedRo
}, },
libraries: { libraries: {
applications: { validateProtectedApplicationById }, applications: { validateProtectedApplicationById },
protectedApps: { addDomainToRemote, syncAppCustomDomainStatus }, protectedApps: { addDomainToRemote, syncAppCustomDomainStatus, syncAppConfigsToRemote },
}, },
}, },
]: RouterInitArgs<T> ]: RouterInitArgs<T>
@ -68,7 +68,7 @@ export default function applicationProtectedAppMetadataRoutes<T extends AuthedRo
const { id } = ctx.guard.params; const { id } = ctx.guard.params;
const { domain } = ctx.guard.body; const { domain } = ctx.guard.body;
const { protectedAppMetadata } = await findApplicationById(id); const { protectedAppMetadata, oidcClientMetadata } = await findApplicationById(id);
assertThat(protectedAppMetadata, 'application.protected_app_not_configured'); assertThat(protectedAppMetadata, 'application.protected_app_not_configured');
assertThat( assertThat(
@ -87,8 +87,23 @@ export default function applicationProtectedAppMetadataRoutes<T extends AuthedRo
const customDomain = await addDomainToRemote(domain); const customDomain = await addDomainToRemote(domain);
await updateApplicationById(id, { await updateApplicationById(id, {
protectedAppMetadata: { ...protectedAppMetadata, customDomains: [customDomain] }, 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; ctx.status = 201;
return next(); return next();
} }