@telicent-oss/fe-auth-lib 1.0.1 → 1.0.2-TELFE-1477.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/AuthServerOAuth2Client.d.ts +12 -15
- package/src/AuthServerOAuth2Client.js +22 -5
- package/src/__tests__/callback.failures.test.ts +285 -0
- package/src/__tests__/callback.success.test.ts +410 -0
- package/src/__tests__/core.failures.test.ts +122 -0
- package/src/__tests__/core.success.test.ts +196 -0
- package/src/__tests__/env.success.test.ts +17 -0
- package/src/__tests__/logout.success.test.ts +151 -0
- package/src/__tests__/methods/base64URLEncode.success.test.ts +39 -0
- package/src/__tests__/methods/generateCodeChallenge.success.test.ts +43 -0
- package/src/__tests__/methods/generateCodeVerifier.success.test.ts +43 -0
- package/src/__tests__/methods/generateNonce.success.test.ts +43 -0
- package/src/__tests__/methods/generateState.success.test.ts +43 -0
- package/src/__tests__/methods/getCsrfToken.failures.test.ts +54 -0
- package/src/__tests__/methods/getCsrfToken.success.test.ts +45 -0
- package/src/__tests__/methods/getRawIdToken.success.test.ts +39 -0
- package/src/__tests__/methods/getUserInfo.failures.test.ts +153 -0
- package/src/__tests__/methods/getUserInfo.success.test.ts +84 -0
- package/src/__tests__/methods/isAuthenticated.failures.test.ts +62 -0
- package/src/__tests__/methods/isAuthenticated.success.test.ts +84 -0
- package/src/__tests__/methods/isIdTokenExpired.failures.test.ts +77 -0
- package/src/__tests__/methods/isIdTokenExpired.success.test.ts +49 -0
- package/src/__tests__/methods/validateIdToken.failures.test.ts +177 -0
- package/src/__tests__/methods/validateIdToken.success.test.ts +55 -0
- package/src/__tests__/methods/validateIdTokenForRecovery.failures.test.ts +121 -0
- package/src/__tests__/methods/validateIdTokenForRecovery.success.test.ts +49 -0
- package/src/__tests__/popup.success.test.ts +277 -0
- package/src/__tests__/request-helpers.failures.test.ts +143 -0
- package/src/__tests__/request-helpers.success.test.ts +137 -0
- package/src/__tests__/schema-loading.failures.test.ts +44 -0
- package/src/__tests__/schema-loading.success.test.ts +106 -0
- package/src/__tests__/state.success.test.ts +217 -0
- package/src/__tests__/test-utils.node.success.test.ts +16 -0
- package/src/__tests__/test-utils.success.test.ts +188 -0
- package/src/__tests__/test-utils.ts +203 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import AuthServerOAuth2Client, {
|
|
2
|
+
AuthServerOAuth2ClientConfig,
|
|
3
|
+
} from "../../AuthServerOAuth2Client";
|
|
4
|
+
import { buildJwt, installTestEnv, resetTestEnv } from "../test-utils";
|
|
5
|
+
|
|
6
|
+
const createConfig = (
|
|
7
|
+
overrides: Partial<AuthServerOAuth2ClientConfig> = {}
|
|
8
|
+
): AuthServerOAuth2ClientConfig => ({
|
|
9
|
+
clientId: "client-1",
|
|
10
|
+
authServerUrl: "http://auth.telicent.localhost",
|
|
11
|
+
redirectUri: "http://app.telicent.localhost/callback",
|
|
12
|
+
popupRedirectUri: "http://app.telicent.localhost/popup",
|
|
13
|
+
scope: "openid profile",
|
|
14
|
+
onLogout: jest.fn(),
|
|
15
|
+
...overrides,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("failure path - validateIdToken errors", () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
installTestEnv();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
resetTestEnv();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("returns false when nonce is missing", () => {
|
|
28
|
+
const now = 1_700_000_000_000;
|
|
29
|
+
jest.spyOn(Date, "now").mockReturnValue(now);
|
|
30
|
+
|
|
31
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
32
|
+
const token = buildJwt({
|
|
33
|
+
sub: "user-1",
|
|
34
|
+
aud: "client-1",
|
|
35
|
+
exp: Math.floor(now / 1000) + 300,
|
|
36
|
+
iat: Math.floor(now / 1000),
|
|
37
|
+
nonce: "ABC_nonce_ABC",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const result = client.validateIdToken(token);
|
|
41
|
+
|
|
42
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
43
|
+
{
|
|
44
|
+
"result": false,
|
|
45
|
+
}
|
|
46
|
+
`);
|
|
47
|
+
|
|
48
|
+
(Date.now as jest.Mock).mockRestore();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("returns false when payload cannot be decoded", () => {
|
|
52
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
53
|
+
|
|
54
|
+
const result = client.validateIdToken("not-a-jwt");
|
|
55
|
+
|
|
56
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
57
|
+
{
|
|
58
|
+
"result": false,
|
|
59
|
+
}
|
|
60
|
+
`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("returns false when nonce does not match", () => {
|
|
64
|
+
const now = 1_700_000_000_000;
|
|
65
|
+
jest.spyOn(Date, "now").mockReturnValue(now);
|
|
66
|
+
|
|
67
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
68
|
+
sessionStorage.setItem("oauth_nonce", "ABC_nonce_ABC");
|
|
69
|
+
const token = buildJwt({
|
|
70
|
+
sub: "user-1",
|
|
71
|
+
aud: "client-1",
|
|
72
|
+
exp: Math.floor(now / 1000) + 300,
|
|
73
|
+
iat: Math.floor(now / 1000),
|
|
74
|
+
nonce: "WRONG_NONCE",
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const result = client.validateIdToken(token);
|
|
78
|
+
|
|
79
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
80
|
+
{
|
|
81
|
+
"result": false,
|
|
82
|
+
}
|
|
83
|
+
`);
|
|
84
|
+
|
|
85
|
+
(Date.now as jest.Mock).mockRestore();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("returns false for audience mismatch", () => {
|
|
89
|
+
const now = 1_700_000_000_000;
|
|
90
|
+
jest.spyOn(Date, "now").mockReturnValue(now);
|
|
91
|
+
|
|
92
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
93
|
+
sessionStorage.setItem("oauth_nonce", "ABC_nonce_ABC");
|
|
94
|
+
const token = buildJwt({
|
|
95
|
+
sub: "user-1",
|
|
96
|
+
aud: "client-2",
|
|
97
|
+
exp: Math.floor(now / 1000) + 300,
|
|
98
|
+
iat: Math.floor(now / 1000),
|
|
99
|
+
nonce: "ABC_nonce_ABC",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const result = client.validateIdToken(token);
|
|
103
|
+
|
|
104
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
105
|
+
{
|
|
106
|
+
"result": false,
|
|
107
|
+
}
|
|
108
|
+
`);
|
|
109
|
+
|
|
110
|
+
(Date.now as jest.Mock).mockRestore();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("returns false for expired token", () => {
|
|
114
|
+
const now = 1_700_000_000_000;
|
|
115
|
+
jest.spyOn(Date, "now").mockReturnValue(now);
|
|
116
|
+
|
|
117
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
118
|
+
sessionStorage.setItem("oauth_nonce", "ABC_nonce_ABC");
|
|
119
|
+
const token = buildJwt({
|
|
120
|
+
sub: "user-1",
|
|
121
|
+
aud: "client-1",
|
|
122
|
+
exp: Math.floor(now / 1000) - 10,
|
|
123
|
+
iat: Math.floor(now / 1000) - 100,
|
|
124
|
+
nonce: "ABC_nonce_ABC",
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const result = client.validateIdToken(token);
|
|
128
|
+
|
|
129
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
130
|
+
{
|
|
131
|
+
"result": false,
|
|
132
|
+
}
|
|
133
|
+
`);
|
|
134
|
+
|
|
135
|
+
(Date.now as jest.Mock).mockRestore();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("returns false for iat too old", () => {
|
|
139
|
+
const now = 1_700_000_000_000;
|
|
140
|
+
jest.spyOn(Date, "now").mockReturnValue(now);
|
|
141
|
+
|
|
142
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
143
|
+
sessionStorage.setItem("oauth_nonce", "ABC_nonce_ABC");
|
|
144
|
+
const token = buildJwt({
|
|
145
|
+
sub: "user-1",
|
|
146
|
+
aud: "client-1",
|
|
147
|
+
exp: Math.floor(now / 1000) + 300,
|
|
148
|
+
iat: Math.floor(now / 1000) - 400,
|
|
149
|
+
nonce: "ABC_nonce_ABC",
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const result = client.validateIdToken(token);
|
|
153
|
+
|
|
154
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
155
|
+
{
|
|
156
|
+
"result": false,
|
|
157
|
+
}
|
|
158
|
+
`);
|
|
159
|
+
|
|
160
|
+
(Date.now as jest.Mock).mockRestore();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("returns false when decodeJWT throws", () => {
|
|
164
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
165
|
+
jest.spyOn(client, "decodeJWT").mockImplementation(() => {
|
|
166
|
+
throw new Error("decode failed");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const result = client.validateIdToken("token");
|
|
170
|
+
|
|
171
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
172
|
+
{
|
|
173
|
+
"result": false,
|
|
174
|
+
}
|
|
175
|
+
`);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import AuthServerOAuth2Client, {
|
|
2
|
+
AuthServerOAuth2ClientConfig,
|
|
3
|
+
} from "../../AuthServerOAuth2Client";
|
|
4
|
+
import { buildJwt, installTestEnv, resetTestEnv } from "../test-utils";
|
|
5
|
+
|
|
6
|
+
const createConfig = (
|
|
7
|
+
overrides: Partial<AuthServerOAuth2ClientConfig> = {}
|
|
8
|
+
): AuthServerOAuth2ClientConfig => ({
|
|
9
|
+
clientId: "client-1",
|
|
10
|
+
authServerUrl: "http://auth.telicent.localhost",
|
|
11
|
+
redirectUri: "http://app.telicent.localhost/callback",
|
|
12
|
+
popupRedirectUri: "http://app.telicent.localhost/popup",
|
|
13
|
+
scope: "openid profile",
|
|
14
|
+
onLogout: jest.fn(),
|
|
15
|
+
...overrides,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("happy path - validateIdToken succeeds", () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
installTestEnv();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
resetTestEnv();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("returns true and clears nonce", () => {
|
|
28
|
+
const now = 1_700_000_000_000;
|
|
29
|
+
jest.spyOn(Date, "now").mockReturnValue(now);
|
|
30
|
+
|
|
31
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
32
|
+
sessionStorage.setItem("oauth_nonce", "ABC_nonce_ABC");
|
|
33
|
+
const token = buildJwt({
|
|
34
|
+
sub: "user-1",
|
|
35
|
+
aud: "client-1",
|
|
36
|
+
exp: Math.floor(now / 1000) + 300,
|
|
37
|
+
iat: Math.floor(now / 1000),
|
|
38
|
+
nonce: "ABC_nonce_ABC",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const result = client.validateIdToken(token);
|
|
42
|
+
|
|
43
|
+
expect({
|
|
44
|
+
result,
|
|
45
|
+
nonceAfter: sessionStorage.getItem("oauth_nonce"),
|
|
46
|
+
}).toMatchInlineSnapshot(`
|
|
47
|
+
{
|
|
48
|
+
"nonceAfter": null,
|
|
49
|
+
"result": true,
|
|
50
|
+
}
|
|
51
|
+
`);
|
|
52
|
+
|
|
53
|
+
(Date.now as jest.Mock).mockRestore();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import AuthServerOAuth2Client, {
|
|
2
|
+
AuthServerOAuth2ClientConfig,
|
|
3
|
+
} from "../../AuthServerOAuth2Client";
|
|
4
|
+
import { buildJwt, installTestEnv, resetTestEnv } from "../test-utils";
|
|
5
|
+
|
|
6
|
+
const createConfig = (
|
|
7
|
+
overrides: Partial<AuthServerOAuth2ClientConfig> = {}
|
|
8
|
+
): AuthServerOAuth2ClientConfig => ({
|
|
9
|
+
clientId: "client-1",
|
|
10
|
+
authServerUrl: "http://auth.telicent.localhost",
|
|
11
|
+
redirectUri: "http://app.telicent.localhost/callback",
|
|
12
|
+
popupRedirectUri: "http://app.telicent.localhost/popup",
|
|
13
|
+
scope: "openid profile",
|
|
14
|
+
onLogout: jest.fn(),
|
|
15
|
+
...overrides,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("failure path - validateIdTokenForRecovery errors", () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
installTestEnv();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
resetTestEnv();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("returns false for expired token", () => {
|
|
28
|
+
const now = 1_700_000_000_000;
|
|
29
|
+
jest.spyOn(Date, "now").mockReturnValue(now);
|
|
30
|
+
|
|
31
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
32
|
+
const token = buildJwt({
|
|
33
|
+
sub: "user-1",
|
|
34
|
+
aud: "client-1",
|
|
35
|
+
exp: Math.floor(now / 1000) - 10,
|
|
36
|
+
iat: Math.floor(now / 1000) - 3600,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const result = client.validateIdTokenForRecovery(token);
|
|
40
|
+
|
|
41
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
42
|
+
{
|
|
43
|
+
"result": false,
|
|
44
|
+
}
|
|
45
|
+
`);
|
|
46
|
+
|
|
47
|
+
(Date.now as jest.Mock).mockRestore();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("returns false when payload cannot be decoded", () => {
|
|
51
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
52
|
+
const result = client.validateIdTokenForRecovery("not-a-jwt");
|
|
53
|
+
|
|
54
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
55
|
+
{
|
|
56
|
+
"result": false,
|
|
57
|
+
}
|
|
58
|
+
`);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("returns false when iat is too old", () => {
|
|
62
|
+
const now = 1_700_000_000_000;
|
|
63
|
+
jest.spyOn(Date, "now").mockReturnValue(now);
|
|
64
|
+
|
|
65
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
66
|
+
const token = buildJwt({
|
|
67
|
+
sub: "user-1",
|
|
68
|
+
aud: "client-1",
|
|
69
|
+
exp: Math.floor(now / 1000) + 300,
|
|
70
|
+
iat: Math.floor(now / 1000) - 7200,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const result = client.validateIdTokenForRecovery(token);
|
|
74
|
+
|
|
75
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
76
|
+
{
|
|
77
|
+
"result": false,
|
|
78
|
+
}
|
|
79
|
+
`);
|
|
80
|
+
|
|
81
|
+
(Date.now as jest.Mock).mockRestore();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("returns false for audience mismatch", () => {
|
|
85
|
+
const now = 1_700_000_000_000;
|
|
86
|
+
jest.spyOn(Date, "now").mockReturnValue(now);
|
|
87
|
+
|
|
88
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
89
|
+
const token = buildJwt({
|
|
90
|
+
sub: "user-1",
|
|
91
|
+
aud: "client-2",
|
|
92
|
+
exp: Math.floor(now / 1000) + 300,
|
|
93
|
+
iat: Math.floor(now / 1000),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const result = client.validateIdTokenForRecovery(token);
|
|
97
|
+
|
|
98
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
99
|
+
{
|
|
100
|
+
"result": false,
|
|
101
|
+
}
|
|
102
|
+
`);
|
|
103
|
+
|
|
104
|
+
(Date.now as jest.Mock).mockRestore();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("returns false when decodeJWT throws", () => {
|
|
108
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
109
|
+
jest.spyOn(client, "decodeJWT").mockImplementation(() => {
|
|
110
|
+
throw new Error("decode failed");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const result = client.validateIdTokenForRecovery("token");
|
|
114
|
+
|
|
115
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
116
|
+
{
|
|
117
|
+
"result": false,
|
|
118
|
+
}
|
|
119
|
+
`);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import AuthServerOAuth2Client, {
|
|
2
|
+
AuthServerOAuth2ClientConfig,
|
|
3
|
+
} from "../../AuthServerOAuth2Client";
|
|
4
|
+
import { buildJwt, installTestEnv, resetTestEnv } from "../test-utils";
|
|
5
|
+
|
|
6
|
+
const createConfig = (
|
|
7
|
+
overrides: Partial<AuthServerOAuth2ClientConfig> = {}
|
|
8
|
+
): AuthServerOAuth2ClientConfig => ({
|
|
9
|
+
clientId: "client-1",
|
|
10
|
+
authServerUrl: "http://auth.telicent.localhost",
|
|
11
|
+
redirectUri: "http://app.telicent.localhost/callback",
|
|
12
|
+
popupRedirectUri: "http://app.telicent.localhost/popup",
|
|
13
|
+
scope: "openid profile",
|
|
14
|
+
onLogout: jest.fn(),
|
|
15
|
+
...overrides,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("happy path - validateIdTokenForRecovery succeeds", () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
installTestEnv();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
resetTestEnv();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("returns true for valid token", () => {
|
|
28
|
+
const now = 1_700_000_000_000;
|
|
29
|
+
jest.spyOn(Date, "now").mockReturnValue(now);
|
|
30
|
+
|
|
31
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
32
|
+
const token = buildJwt({
|
|
33
|
+
sub: "user-1",
|
|
34
|
+
aud: "client-1",
|
|
35
|
+
exp: Math.floor(now / 1000) + 300,
|
|
36
|
+
iat: Math.floor(now / 1000),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const result = client.validateIdTokenForRecovery(token);
|
|
40
|
+
|
|
41
|
+
expect({ result }).toMatchInlineSnapshot(`
|
|
42
|
+
{
|
|
43
|
+
"result": true,
|
|
44
|
+
}
|
|
45
|
+
`);
|
|
46
|
+
|
|
47
|
+
(Date.now as jest.Mock).mockRestore();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import AuthServerOAuth2Client, {
|
|
2
|
+
AuthServerOAuth2ClientConfig,
|
|
3
|
+
} from "../AuthServerOAuth2Client";
|
|
4
|
+
import { installTestEnv, resetTestEnv } from "./test-utils";
|
|
5
|
+
|
|
6
|
+
const createConfig = (
|
|
7
|
+
overrides: Partial<AuthServerOAuth2ClientConfig> = {}
|
|
8
|
+
): AuthServerOAuth2ClientConfig => ({
|
|
9
|
+
clientId: "client-1",
|
|
10
|
+
authServerUrl: "http://auth.telicent.localhost",
|
|
11
|
+
redirectUri: "http://app.telicent.localhost/callback",
|
|
12
|
+
popupRedirectUri: "http://app.telicent.localhost/popup",
|
|
13
|
+
scope: "openid profile",
|
|
14
|
+
onLogout: jest.fn(),
|
|
15
|
+
...overrides,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const createPopup = (): Window =>
|
|
19
|
+
({
|
|
20
|
+
close: jest.fn(),
|
|
21
|
+
closed: false,
|
|
22
|
+
} as unknown as Window);
|
|
23
|
+
|
|
24
|
+
describe("happy path - popup flow and callbacks", () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
installTestEnv();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
resetTestEnv();
|
|
31
|
+
jest.useRealTimers();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("dispatches oauth-success and closes popup", () => {
|
|
35
|
+
jest.useFakeTimers();
|
|
36
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
37
|
+
const popup = createPopup();
|
|
38
|
+
const dispatchSpy = jest.spyOn(window, "dispatchEvent");
|
|
39
|
+
const removeSpy = jest.spyOn(window, "removeEventListener");
|
|
40
|
+
|
|
41
|
+
client.startPopupFlow(popup);
|
|
42
|
+
window.dispatchEvent(
|
|
43
|
+
new MessageEvent("message", {
|
|
44
|
+
data: { type: "oauth-success", clientId: "client-1" },
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect({
|
|
49
|
+
closeCalls: (popup.close as jest.Mock).mock.calls.length,
|
|
50
|
+
removeCalls: removeSpy.mock.calls.length,
|
|
51
|
+
dispatched: dispatchSpy.mock.calls.map((call) => ({
|
|
52
|
+
type: call[0].type,
|
|
53
|
+
detail: (call[0] as CustomEvent).detail,
|
|
54
|
+
})),
|
|
55
|
+
}).toMatchInlineSnapshot(`
|
|
56
|
+
{
|
|
57
|
+
"closeCalls": 1,
|
|
58
|
+
"dispatched": [
|
|
59
|
+
{
|
|
60
|
+
"detail": undefined,
|
|
61
|
+
"type": "message",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"detail": {
|
|
65
|
+
"clientId": "client-1",
|
|
66
|
+
"type": "oauth-success",
|
|
67
|
+
},
|
|
68
|
+
"type": "oauth-success",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
"removeCalls": 1,
|
|
72
|
+
}
|
|
73
|
+
`);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("dispatches oauth-error and closes popup", () => {
|
|
77
|
+
jest.useFakeTimers();
|
|
78
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
79
|
+
const popup = createPopup();
|
|
80
|
+
const dispatchSpy = jest.spyOn(window, "dispatchEvent");
|
|
81
|
+
|
|
82
|
+
client.startPopupFlow(popup);
|
|
83
|
+
window.dispatchEvent(
|
|
84
|
+
new MessageEvent("message", {
|
|
85
|
+
data: { type: "oauth-error", clientId: "client-1", message: "fail" },
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
expect({
|
|
90
|
+
closeCalls: (popup.close as jest.Mock).mock.calls.length,
|
|
91
|
+
dispatched: dispatchSpy.mock.calls.map((call) => ({
|
|
92
|
+
type: call[0].type,
|
|
93
|
+
detail: (call[0] as CustomEvent).detail,
|
|
94
|
+
})),
|
|
95
|
+
}).toMatchInlineSnapshot(`
|
|
96
|
+
{
|
|
97
|
+
"closeCalls": 1,
|
|
98
|
+
"dispatched": [
|
|
99
|
+
{
|
|
100
|
+
"detail": undefined,
|
|
101
|
+
"type": "message",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"detail": {
|
|
105
|
+
"clientId": "client-1",
|
|
106
|
+
"type": "oauth-success",
|
|
107
|
+
},
|
|
108
|
+
"type": "oauth-success",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"detail": undefined,
|
|
112
|
+
"type": "message",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"detail": {
|
|
116
|
+
"clientId": "client-1",
|
|
117
|
+
"message": "fail",
|
|
118
|
+
"type": "oauth-error",
|
|
119
|
+
},
|
|
120
|
+
"type": "oauth-error",
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
}
|
|
124
|
+
`);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("ignores messages from other clients", () => {
|
|
128
|
+
jest.useFakeTimers();
|
|
129
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
130
|
+
const popup = createPopup();
|
|
131
|
+
const dispatchSpy = jest.spyOn(window, "dispatchEvent");
|
|
132
|
+
|
|
133
|
+
client.startPopupFlow(popup);
|
|
134
|
+
window.dispatchEvent(
|
|
135
|
+
new MessageEvent("message", {
|
|
136
|
+
data: { type: "oauth-success", clientId: "other-client" },
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
expect({
|
|
141
|
+
closeCalls: (popup.close as jest.Mock).mock.calls.length,
|
|
142
|
+
dispatchCalls: dispatchSpy.mock.calls.length,
|
|
143
|
+
}).toMatchInlineSnapshot(`
|
|
144
|
+
{
|
|
145
|
+
"closeCalls": 0,
|
|
146
|
+
"dispatchCalls": 5,
|
|
147
|
+
}
|
|
148
|
+
`);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("dispatches oauth-callback and closes popup", () => {
|
|
152
|
+
jest.useFakeTimers();
|
|
153
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
154
|
+
const popup = createPopup();
|
|
155
|
+
const dispatchSpy = jest.spyOn(window, "dispatchEvent");
|
|
156
|
+
|
|
157
|
+
client.startPopupFlow(popup);
|
|
158
|
+
window.dispatchEvent(
|
|
159
|
+
new MessageEvent("message", {
|
|
160
|
+
data: {
|
|
161
|
+
type: "oauth-callback",
|
|
162
|
+
clientId: "client-1",
|
|
163
|
+
callbackUrl: "http://app.telicent.localhost/callback?code=1",
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
expect({
|
|
169
|
+
closeCalls: (popup.close as jest.Mock).mock.calls.length,
|
|
170
|
+
dispatched: dispatchSpy.mock.calls.map((call) => ({
|
|
171
|
+
type: call[0].type,
|
|
172
|
+
detail: (call[0] as CustomEvent).detail,
|
|
173
|
+
})),
|
|
174
|
+
}).toMatchInlineSnapshot(`
|
|
175
|
+
{
|
|
176
|
+
"closeCalls": 1,
|
|
177
|
+
"dispatched": [
|
|
178
|
+
{
|
|
179
|
+
"detail": undefined,
|
|
180
|
+
"type": "message",
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"detail": {
|
|
184
|
+
"clientId": "client-1",
|
|
185
|
+
"type": "oauth-success",
|
|
186
|
+
},
|
|
187
|
+
"type": "oauth-success",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"detail": undefined,
|
|
191
|
+
"type": "message",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"detail": {
|
|
195
|
+
"clientId": "client-1",
|
|
196
|
+
"message": "fail",
|
|
197
|
+
"type": "oauth-error",
|
|
198
|
+
},
|
|
199
|
+
"type": "oauth-error",
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"detail": undefined,
|
|
203
|
+
"type": "message",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"detail": undefined,
|
|
207
|
+
"type": "message",
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"detail": {
|
|
211
|
+
"callbackUrl": "http://app.telicent.localhost/callback?code=1",
|
|
212
|
+
"clientId": "client-1",
|
|
213
|
+
"type": "oauth-callback",
|
|
214
|
+
},
|
|
215
|
+
"type": "oauth-callback",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"detail": {
|
|
219
|
+
"callbackUrl": "http://app.telicent.localhost/callback?code=1",
|
|
220
|
+
"clientId": "client-1",
|
|
221
|
+
"type": "oauth-callback",
|
|
222
|
+
},
|
|
223
|
+
"type": "oauth-callback",
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
}
|
|
227
|
+
`);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("posts callback data to opener", () => {
|
|
231
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
232
|
+
const postMessage = jest.fn();
|
|
233
|
+
Object.defineProperty(window, "opener", {
|
|
234
|
+
value: { postMessage },
|
|
235
|
+
writable: true,
|
|
236
|
+
});
|
|
237
|
+
Object.defineProperty(window, "location", {
|
|
238
|
+
value: { href: "http://app.telicent.localhost/callback?code=123" },
|
|
239
|
+
writable: true,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
client.finishPopupFlow();
|
|
243
|
+
|
|
244
|
+
expect({ postMessageArgs: postMessage.mock.calls[0] })
|
|
245
|
+
.toMatchInlineSnapshot(`
|
|
246
|
+
{
|
|
247
|
+
"postMessageArgs": [
|
|
248
|
+
{
|
|
249
|
+
"callbackUrl": "http://app.telicent.localhost/callback?code=123",
|
|
250
|
+
"clientId": "client-1",
|
|
251
|
+
"type": "oauth-callback",
|
|
252
|
+
},
|
|
253
|
+
"*",
|
|
254
|
+
],
|
|
255
|
+
}
|
|
256
|
+
`);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("cleans up listeners when popup closes", () => {
|
|
260
|
+
jest.useFakeTimers();
|
|
261
|
+
const client = new AuthServerOAuth2Client(createConfig());
|
|
262
|
+
const popup = createPopup() as Window & { closed: boolean };
|
|
263
|
+
const removeSpy = jest.spyOn(window, "removeEventListener");
|
|
264
|
+
|
|
265
|
+
client.startPopupFlow(popup);
|
|
266
|
+
popup.closed = true;
|
|
267
|
+
jest.advanceTimersByTime(1000);
|
|
268
|
+
|
|
269
|
+
expect({
|
|
270
|
+
removeCalls: removeSpy.mock.calls.length,
|
|
271
|
+
}).toMatchInlineSnapshot(`
|
|
272
|
+
{
|
|
273
|
+
"removeCalls": 5,
|
|
274
|
+
}
|
|
275
|
+
`);
|
|
276
|
+
});
|
|
277
|
+
});
|