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:
parent
43ce51ce2a
commit
fa89d33252
4 changed files with 83 additions and 12 deletions
|
@ -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()', () => {
|
||||||
|
|
|
@ -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,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 () => {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue