Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/util/auth-check-required-sso.ts
5838 views
1
/*
2
* This file is part of CoCalc: Copyright © 2022-2025 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { is_valid_email_address } from "@cocalc/util/misc";
7
import { Strategy } from "@cocalc/util/types/sso";
8
9
interface Opts {
10
email: string | undefined;
11
strategies: Strategy[] | undefined;
12
specificStrategy?: string;
13
}
14
15
/**
16
* If the domain of a given email address belongs to an SSO strategy,
17
* which is configured to be an "exclusive" domain, then return the Strategy.
18
* This also matches subdomains, i.e. "[email protected]" is goverend by "baz.edu",
19
* while "[email protected]" is NOT goverend by "baz.edu".
20
*
21
* Special case: an sso domain "*" covers all domains, not covered by any other
22
* exclusive SSO strategy. If there is just one such "*"-SSO strategy, it will deal with all
23
* accounts.
24
*
25
* Optionally, if @specificStrategy is set, only that strategy or "*" is checked!
26
*/
27
export function checkRequiredSSO(opts: Opts): Strategy | undefined {
28
const { email, strategies, specificStrategy } = opts;
29
// if the domain of email is contained in any of the strategie's exclusiveDomain array, return that strategy's name
30
if (!email) return;
31
if (strategies == null || strategies.length === 0) return;
32
if (email.indexOf("@") === -1) return;
33
if (!is_valid_email_address(email)) return;
34
const emailDomain = getEmailDomain(email);
35
if (!emailDomain) return;
36
for (const strategy of strategies) {
37
if (specificStrategy && specificStrategy !== strategy.name) continue;
38
for (const ssoDomain of strategy.exclusiveDomains) {
39
if (ssoDomain === "*") continue; // dealt with below
40
if (emailBelongsToDomain(emailDomain, ssoDomain)) {
41
return strategy;
42
}
43
}
44
}
45
// At this point, we either matched an existing strategy (above) or there is a "*" strategy
46
for (const strategy of strategies) {
47
if (specificStrategy && specificStrategy !== strategy.name) continue;
48
if (strategy.exclusiveDomains.includes("*")) {
49
return strategy;
50
}
51
}
52
}
53
54
export function getEmailDomain(email: string): string {
55
return email.trim().toLowerCase().split("@")[1];
56
}
57
58
/**
59
* This checks if the email's domain is either exactly the ssoDomain or a subdomain.
60
* E.g. for "foo.edu", an email "[email protected]" is covered as well.
61
* Note: Both emailDomain (from getEmailDomain) and ssoDomain (from database queries)
62
* are normalized to lowercase, so direct comparison is safe.
63
*/
64
export function emailBelongsToDomain(
65
emailDomain: string,
66
ssoDomain: string,
67
): boolean {
68
return emailDomain === ssoDomain || emailDomain.endsWith(`.${ssoDomain}`);
69
}
70
71