Skip to content

Commit

Permalink
feat(core): implement get sso connectors
Browse files Browse the repository at this point in the history
implement get sso connectors endpoint
  • Loading branch information
simeng-li committed Jul 15, 2024
1 parent 5bae495 commit b0f0dc4
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class SignInExperienceValidator {
}
}

async verifyIdentificationMethod(
public async verifyIdentificationMethod(
event: InteractionEvent,
verificationRecord: VerificationRecord
) {
Expand All @@ -93,7 +93,21 @@ export class SignInExperienceValidator {
await this.guardSsoOnlyEmailIdentifier(verificationRecord);
}

private async getSignInExperienceData() {
public async getEnabledSsoConnectorsByEmail(email: string) {
const domain = email.split('@')[1];
const { singleSignOnEnabled } = await this.getSignInExperienceData();

if (!singleSignOnEnabled || !domain) {
return [];
}

Check warning on line 102 in packages/core/src/routes/experience/classes/validators/sign-in-experience-validator.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/routes/experience/classes/validators/sign-in-experience-validator.ts#L101-L102

Added lines #L101 - L102 were not covered by tests

const { getAvailableSsoConnectors } = this.libraries.ssoConnectors;
const availableSsoConnectors = await getAvailableSsoConnectors();

return availableSsoConnectors.filter(({ domains }) => domains.includes(domain));
}

public async getSignInExperienceData() {
this.signInExperienceDataCache ||=
await this.queries.signInExperiences.findDefaultSignInExperience();

Expand All @@ -117,29 +131,17 @@ export class SignInExperienceValidator {
return;
}

const domain = emailIdentifier.split('@')[1];
const { singleSignOnEnabled } = await this.getSignInExperienceData();

if (!singleSignOnEnabled || !domain) {
return;
}

const { getAvailableSsoConnectors } = this.libraries.ssoConnectors;
const availableSsoConnectors = await getAvailableSsoConnectors();

const domainEnabledConnectors = availableSsoConnectors.filter(({ domains }) =>
domains.includes(domain)
);
const enabledSsoConnectors = await this.getEnabledSsoConnectorsByEmail(emailIdentifier);

assertThat(
domainEnabledConnectors.length === 0,
enabledSsoConnectors.length === 0,
new RequestError(
{
code: 'session.sso_enabled',
status: 422,
},
{
ssoConnectors: domainEnabledConnectors,
ssoConnectors: enabledSsoConnectors,
}
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,36 @@ export default function enterpriseSsoVerificationRoutes<T extends WithLogContext
return next();
}
);

router.get(
`${experienceRoutes.verification}/sso/connectors`,
koaGuard({
query: z.object({
email: z.string().email(),
}),
status: [200, 400],
response: z.object({
connectorIds: z.string().array(),
}),
}),
async (ctx, next) => {
const { email } = ctx.guard.query;
const {
experienceInteraction: { signInExperienceValidator },
} = ctx;

assertThat(
email.split('@')[1],
new RequestError({ code: 'guard.invalid_input', status: 400, email })
);

const connectors = await signInExperienceValidator.getEnabledSsoConnectorsByEmail(email);

ctx.body = {
connectorIds: connectors.map(({ id }) => id),
};

return next();
}

Check warning on line 133 in packages/core/src/routes/experience/verification-routes/enterprise-sso-verification.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/routes/experience/verification-routes/enterprise-sso-verification.ts#L116-L133

Added lines #L116 - L133 were not covered by tests
);
}
9 changes: 9 additions & 0 deletions packages/integration-tests/src/client/experience/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,15 @@ export class ExperienceClient extends MockClient {
.json<{ verificationId: string }>();
}

public async getAvailableSsoConnectors(email: string) {
return api
.get(`${experienceRoutes.verification}/sso/connectors`, {
headers: { cookie: this.interactionCookie },
searchParams: { email },
})
.json<{ connectorIds: string[] }>();
}

public async createTotpSecret() {
return api
.post(`${experienceRoutes.verification}/totp/secret`, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,50 @@ devFeatureTest.describe('enterprise sso verification', () => {
});
});
});

describe('getSsoConnectorsByEmail', () => {
const ssoConnectorApi = new SsoConnectorApi();
const domain = `foo${randomString()}.com`;

beforeAll(async () => {
await ssoConnectorApi.createMockOidcConnector([domain]);

await updateSignInExperience({
singleSignOnEnabled: true,
});
});

afterAll(async () => {
await ssoConnectorApi.cleanUp();
});

it('should get sso connectors with given email properly', async () => {
const client = await initExperienceClient();

const response = await client.getAvailableSsoConnectors('bar@' + domain);

expect(response.connectorIds.length).toBeGreaterThan(0);
expect(response.connectorIds[0]).toBe(ssoConnectorApi.firstConnectorId);
});

it('should return empty array if no sso connectors found', async () => {
const client = await initExperienceClient();

const response = await client.getAvailableSsoConnectors('[email protected]');

expect(response.connectorIds.length).toBe(0);
});

it('should return empty array if sso is not enabled', async () => {
await updateSignInExperience({
singleSignOnEnabled: false,
});

const client = await initExperienceClient();

const response = await client.getAvailableSsoConnectors('bar@' + domain);

expect(response.connectorIds.length).toBe(0);
});
});
});

0 comments on commit b0f0dc4

Please sign in to comment.