oidc-spa 8.2.11 → 8.3.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/README.md +58 -6
- package/core/createOidc.js +9 -2
- package/core/createOidc.js.map +1 -1
- package/core/earlyInit.d.ts +6 -2
- package/core/earlyInit.js +157 -32
- package/core/earlyInit.js.map +1 -1
- package/core/loginSilent.js +7 -42
- package/core/loginSilent.js.map +1 -1
- package/esm/core/createOidc.js +9 -2
- package/esm/core/createOidc.js.map +1 -1
- package/esm/core/earlyInit.d.ts +6 -2
- package/esm/core/earlyInit.js +156 -32
- package/esm/core/earlyInit.js.map +1 -1
- package/esm/core/loginSilent.js +7 -42
- package/esm/core/loginSilent.js.map +1 -1
- package/esm/tanstack-start/react/withHandlingOidcPostLoginNavigation.js +13 -2
- package/esm/tanstack-start/react/withHandlingOidcPostLoginNavigation.js.map +1 -1
- package/esm/tools/Evt.js +18 -10
- package/esm/tools/Evt.js.map +1 -1
- package/package.json +2 -2
- package/src/core/createOidc.ts +8 -1
- package/src/core/earlyInit.ts +205 -42
- package/src/core/loginSilent.ts +18 -79
- package/src/tanstack-start/react/withHandlingOidcPostLoginNavigation.tsx +13 -2
- package/src/tools/Evt.ts +17 -16
- package/src/vite-plugin/handleClientEntrypoint.ts +4 -6
- package/tools/Evt.js +18 -10
- package/tools/Evt.js.map +1 -1
- package/vite-plugin/handleClientEntrypoint.js +3 -1
- package/vite-plugin/handleClientEntrypoint.js.map +1 -1
- package/core/iframeMessageProtection.d.ts +0 -29
- package/core/iframeMessageProtection.js +0 -129
- package/core/iframeMessageProtection.js.map +0 -1
- package/esm/core/iframeMessageProtection.d.ts +0 -29
- package/esm/core/iframeMessageProtection.js +0 -123
- package/esm/core/iframeMessageProtection.js.map +0 -1
- package/esm/tools/asymmetricEncryption.d.ts +0 -18
- package/esm/tools/asymmetricEncryption.js +0 -85
- package/esm/tools/asymmetricEncryption.js.map +0 -1
- package/src/core/iframeMessageProtection.ts +0 -186
- package/src/tools/asymmetricEncryption.ts +0 -184
- package/tools/asymmetricEncryption.d.ts +0 -18
- package/tools/asymmetricEncryption.js +0 -90
- package/tools/asymmetricEncryption.js.map +0 -1
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { assert } from "../tools/tsafe/assert";
|
|
2
|
-
import { asymmetricEncrypt, asymmetricDecrypt, generateKeys } from "../tools/asymmetricEncryption";
|
|
3
|
-
let capturedApis = undefined;
|
|
4
|
-
export function captureApisForIframeProtection() {
|
|
5
|
-
capturedApis = {
|
|
6
|
-
setItem: Storage.prototype.setItem,
|
|
7
|
-
sessionStorage: window.sessionStorage,
|
|
8
|
-
setTimeout: window.setTimeout,
|
|
9
|
-
alert: window.alert
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
const SESSION_STORAGE_PREFIX = "oidc-spa_iframe_authResponse_publicKey_";
|
|
13
|
-
export function preventSessionStorageSetItemOfPublicKeyByThirdParty() {
|
|
14
|
-
const setItem_protected = function setItem(key, value) {
|
|
15
|
-
if (key.startsWith(SESSION_STORAGE_PREFIX)) {
|
|
16
|
-
throw new Error("Attack prevented by oidc-spa. You have malicious code running in your system");
|
|
17
|
-
}
|
|
18
|
-
assert(capturedApis !== undefined);
|
|
19
|
-
return capturedApis.setItem.call(this, key, value);
|
|
20
|
-
};
|
|
21
|
-
{
|
|
22
|
-
const pd = Object.getOwnPropertyDescriptor(Storage.prototype, "setItem");
|
|
23
|
-
assert(pd !== undefined);
|
|
24
|
-
Object.defineProperty(Storage.prototype, "setItem", {
|
|
25
|
-
enumerable: pd.enumerable,
|
|
26
|
-
writable: pd.writable,
|
|
27
|
-
value: setItem_protected
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function getSessionStorageKey(params) {
|
|
32
|
-
const { stateUrlParamValue } = params;
|
|
33
|
-
return `${SESSION_STORAGE_PREFIX}${stateUrlParamValue}`;
|
|
34
|
-
}
|
|
35
|
-
const ENCRYPTED_AUTH_RESPONSES_PREFIX = "oidc-spa_encrypted_authResponse_";
|
|
36
|
-
function getIsEncryptedAuthResponse(params) {
|
|
37
|
-
const { message, stateUrlParamValue } = params;
|
|
38
|
-
return (typeof message === "string" &&
|
|
39
|
-
message.startsWith(`${ENCRYPTED_AUTH_RESPONSES_PREFIX}${stateUrlParamValue}`));
|
|
40
|
-
}
|
|
41
|
-
function getReadyMessage(params) {
|
|
42
|
-
const { stateUrlParamValue } = params;
|
|
43
|
-
return `oidc-spa_ready_to_read_publicKey_${stateUrlParamValue}`;
|
|
44
|
-
}
|
|
45
|
-
function getIsReadyToReadPublicKeyMessage(params) {
|
|
46
|
-
const { message, stateUrlParamValue } = params;
|
|
47
|
-
return message === getReadyMessage({ stateUrlParamValue });
|
|
48
|
-
}
|
|
49
|
-
export async function initIframeMessageProtection(params) {
|
|
50
|
-
const { stateUrlParamValue } = params;
|
|
51
|
-
const { publicKey, privateKey } = await generateKeys();
|
|
52
|
-
const sessionStorageKey = getSessionStorageKey({ stateUrlParamValue });
|
|
53
|
-
let timer = undefined;
|
|
54
|
-
function setSessionStoragePublicKey() {
|
|
55
|
-
assert(capturedApis !== undefined);
|
|
56
|
-
const { setItem } = capturedApis;
|
|
57
|
-
setItem.call(capturedApis.sessionStorage, sessionStorageKey, publicKey);
|
|
58
|
-
}
|
|
59
|
-
function startSessionStoragePublicKeyMaliciousWriteDetection() {
|
|
60
|
-
assert(capturedApis !== undefined);
|
|
61
|
-
const { alert, setTimeout } = capturedApis;
|
|
62
|
-
sessionStorage.removeItem(sessionStorageKey);
|
|
63
|
-
const checkTimeoutCallback = () => {
|
|
64
|
-
const publicKey_inStorage = sessionStorage.getItem(sessionStorageKey);
|
|
65
|
-
if (publicKey_inStorage !== null && publicKey_inStorage !== publicKey) {
|
|
66
|
-
while (true) {
|
|
67
|
-
alert([
|
|
68
|
-
"⚠️ Security Alert:",
|
|
69
|
-
"oidc-spa detected an attack attempt.",
|
|
70
|
-
"For your safety, please close this tab immediately",
|
|
71
|
-
"and notify the site administrator."
|
|
72
|
-
].join(" "));
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
check();
|
|
76
|
-
};
|
|
77
|
-
function check() {
|
|
78
|
-
timer = setTimeout(checkTimeoutCallback, 5);
|
|
79
|
-
}
|
|
80
|
-
check();
|
|
81
|
-
}
|
|
82
|
-
async function decodeEncryptedAuth(params) {
|
|
83
|
-
const { encryptedAuthResponse } = params;
|
|
84
|
-
const { message: authResponse_str } = await asymmetricDecrypt({
|
|
85
|
-
encryptedMessage: encryptedAuthResponse.slice(ENCRYPTED_AUTH_RESPONSES_PREFIX.length + stateUrlParamValue.length),
|
|
86
|
-
privateKey
|
|
87
|
-
});
|
|
88
|
-
const authResponse = JSON.parse(authResponse_str);
|
|
89
|
-
return { authResponse };
|
|
90
|
-
}
|
|
91
|
-
function clearSessionStoragePublicKey() {
|
|
92
|
-
sessionStorage.removeItem(sessionStorageKey);
|
|
93
|
-
clearTimeout(timer);
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
getIsReadyToReadPublicKeyMessage,
|
|
97
|
-
startSessionStoragePublicKeyMaliciousWriteDetection,
|
|
98
|
-
setSessionStoragePublicKey,
|
|
99
|
-
getIsEncryptedAuthResponse,
|
|
100
|
-
decodeEncryptedAuth,
|
|
101
|
-
clearSessionStoragePublicKey
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
export async function postEncryptedAuthResponseToParent(params) {
|
|
105
|
-
const { authResponse } = params;
|
|
106
|
-
parent.postMessage(getReadyMessage({ stateUrlParamValue: authResponse.state }), location.origin);
|
|
107
|
-
await new Promise(resolve => setTimeout(resolve, 2));
|
|
108
|
-
let publicKey;
|
|
109
|
-
{
|
|
110
|
-
let sessionStorageKey = getSessionStorageKey({ stateUrlParamValue: authResponse.state });
|
|
111
|
-
while ((publicKey = sessionStorage.getItem(sessionStorageKey)) === null) {
|
|
112
|
-
await new Promise(resolve => setTimeout(resolve, 2));
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
await new Promise(resolve => setTimeout(resolve, 7));
|
|
116
|
-
const { encryptedMessage: encryptedMessage_withoutPrefix } = await asymmetricEncrypt({
|
|
117
|
-
publicKey,
|
|
118
|
-
message: JSON.stringify(authResponse)
|
|
119
|
-
});
|
|
120
|
-
const encryptedMessage = `${ENCRYPTED_AUTH_RESPONSES_PREFIX}${authResponse.state}${encryptedMessage_withoutPrefix}`;
|
|
121
|
-
parent.postMessage(encryptedMessage, location.origin);
|
|
122
|
-
}
|
|
123
|
-
//# sourceMappingURL=iframeMessageProtection.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"iframeMessageProtection.js","sourceRoot":"","sources":["../../src/core/iframeMessageProtection.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
type AsymmetricKeys = {
|
|
2
|
-
publicKey: string;
|
|
3
|
-
privateKey: string;
|
|
4
|
-
};
|
|
5
|
-
export declare function generateKeys(): Promise<AsymmetricKeys>;
|
|
6
|
-
export declare function asymmetricEncrypt(params: {
|
|
7
|
-
publicKey: string;
|
|
8
|
-
message: string;
|
|
9
|
-
}): Promise<{
|
|
10
|
-
encryptedMessage: string;
|
|
11
|
-
}>;
|
|
12
|
-
export declare function asymmetricDecrypt(params: {
|
|
13
|
-
privateKey: string;
|
|
14
|
-
encryptedMessage: string;
|
|
15
|
-
}): Promise<{
|
|
16
|
-
message: string;
|
|
17
|
-
}>;
|
|
18
|
-
export {};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
const INFO_LABEL = "oidc-spa/tools/asymmetricEncryption";
|
|
2
|
-
export async function generateKeys() {
|
|
3
|
-
const keyPair = await crypto.subtle.generateKey({
|
|
4
|
-
name: "ECDH",
|
|
5
|
-
namedCurve: "P-256"
|
|
6
|
-
}, true, ["deriveKey", "deriveBits"]);
|
|
7
|
-
const publicKeyRaw = await crypto.subtle.exportKey("jwk", keyPair.publicKey);
|
|
8
|
-
const privateKeyRaw = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
|
|
9
|
-
return {
|
|
10
|
-
publicKey: btoa(JSON.stringify(publicKeyRaw)),
|
|
11
|
-
privateKey: btoa(JSON.stringify(privateKeyRaw))
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
export async function asymmetricEncrypt(params) {
|
|
15
|
-
const { publicKey, message } = params;
|
|
16
|
-
const importedPublicKey = await crypto.subtle.importKey("jwk", JSON.parse(atob(publicKey)), {
|
|
17
|
-
name: "ECDH",
|
|
18
|
-
namedCurve: "P-256"
|
|
19
|
-
}, false, []);
|
|
20
|
-
const ephemeralKeyPair = await crypto.subtle.generateKey({
|
|
21
|
-
name: "ECDH",
|
|
22
|
-
namedCurve: "P-256"
|
|
23
|
-
}, true, ["deriveKey", "deriveBits"]);
|
|
24
|
-
const sharedSecret = await crypto.subtle.deriveBits({
|
|
25
|
-
name: "ECDH",
|
|
26
|
-
public: importedPublicKey
|
|
27
|
-
}, ephemeralKeyPair.privateKey, 256);
|
|
28
|
-
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
29
|
-
const infoBytes = new TextEncoder().encode(INFO_LABEL);
|
|
30
|
-
const hkdfKey = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, ["deriveKey"]);
|
|
31
|
-
const derivedKey = await crypto.subtle.deriveKey({
|
|
32
|
-
name: "HKDF",
|
|
33
|
-
hash: "SHA-256",
|
|
34
|
-
salt,
|
|
35
|
-
info: infoBytes
|
|
36
|
-
}, hkdfKey, { name: "AES-GCM", length: 256 }, false, ["encrypt"]);
|
|
37
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
38
|
-
const encodedMessage = new TextEncoder().encode(message);
|
|
39
|
-
const ciphertext = await crypto.subtle.encrypt({
|
|
40
|
-
name: "AES-GCM",
|
|
41
|
-
iv
|
|
42
|
-
}, derivedKey, encodedMessage);
|
|
43
|
-
const ephemeralPubKeyRaw = await crypto.subtle.exportKey("jwk", ephemeralKeyPair.publicKey);
|
|
44
|
-
const payload = {
|
|
45
|
-
ephemeralPubKey: ephemeralPubKeyRaw,
|
|
46
|
-
iv: Array.from(iv),
|
|
47
|
-
salt: Array.from(salt),
|
|
48
|
-
ciphertext: Array.from(new Uint8Array(ciphertext))
|
|
49
|
-
};
|
|
50
|
-
return {
|
|
51
|
-
encryptedMessage: btoa(JSON.stringify(payload))
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
export async function asymmetricDecrypt(params) {
|
|
55
|
-
const { privateKey, encryptedMessage } = params;
|
|
56
|
-
const { ephemeralPubKey, iv, salt, ciphertext } = JSON.parse(atob(encryptedMessage));
|
|
57
|
-
const importedPrivateKey = await crypto.subtle.importKey("jwk", JSON.parse(atob(privateKey)), {
|
|
58
|
-
name: "ECDH",
|
|
59
|
-
namedCurve: "P-256"
|
|
60
|
-
}, false, ["deriveKey", "deriveBits"]);
|
|
61
|
-
const importedEphemeralPubKey = await crypto.subtle.importKey("jwk", ephemeralPubKey, {
|
|
62
|
-
name: "ECDH",
|
|
63
|
-
namedCurve: "P-256"
|
|
64
|
-
}, false, []);
|
|
65
|
-
const sharedSecret = await crypto.subtle.deriveBits({
|
|
66
|
-
name: "ECDH",
|
|
67
|
-
public: importedEphemeralPubKey
|
|
68
|
-
}, importedPrivateKey, 256);
|
|
69
|
-
const infoBytes = new TextEncoder().encode(INFO_LABEL);
|
|
70
|
-
const hkdfKey = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, ["deriveKey"]);
|
|
71
|
-
const derivedKey = await crypto.subtle.deriveKey({
|
|
72
|
-
name: "HKDF",
|
|
73
|
-
hash: "SHA-256",
|
|
74
|
-
salt: new Uint8Array(salt),
|
|
75
|
-
info: infoBytes
|
|
76
|
-
}, hkdfKey, { name: "AES-GCM", length: 256 }, false, ["decrypt"]);
|
|
77
|
-
const decryptedBuffer = await crypto.subtle.decrypt({
|
|
78
|
-
name: "AES-GCM",
|
|
79
|
-
iv: new Uint8Array(iv)
|
|
80
|
-
}, derivedKey, new Uint8Array(ciphertext));
|
|
81
|
-
return {
|
|
82
|
-
message: new TextDecoder().decode(decryptedBuffer)
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
//# sourceMappingURL=asymmetricEncryption.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"asymmetricEncryption.js","sourceRoot":"","sources":["../../src/tools/asymmetricEncryption.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { assert } from "../tools/tsafe/assert";
|
|
2
|
-
import { asymmetricEncrypt, asymmetricDecrypt, generateKeys } from "../tools/asymmetricEncryption";
|
|
3
|
-
import { type AuthResponse } from "./AuthResponse";
|
|
4
|
-
|
|
5
|
-
let capturedApis:
|
|
6
|
-
| {
|
|
7
|
-
setItem: typeof localStorage.setItem;
|
|
8
|
-
sessionStorage: typeof window.sessionStorage;
|
|
9
|
-
setTimeout: typeof window.setTimeout;
|
|
10
|
-
alert: typeof window.alert;
|
|
11
|
-
}
|
|
12
|
-
| undefined = undefined;
|
|
13
|
-
|
|
14
|
-
export function captureApisForIframeProtection() {
|
|
15
|
-
capturedApis = {
|
|
16
|
-
setItem: Storage.prototype.setItem,
|
|
17
|
-
sessionStorage: window.sessionStorage,
|
|
18
|
-
setTimeout: window.setTimeout,
|
|
19
|
-
alert: window.alert
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const SESSION_STORAGE_PREFIX = "oidc-spa_iframe_authResponse_publicKey_";
|
|
24
|
-
|
|
25
|
-
export function preventSessionStorageSetItemOfPublicKeyByThirdParty() {
|
|
26
|
-
const setItem_protected = function setItem(this: any, key: string, value: string): void {
|
|
27
|
-
if (key.startsWith(SESSION_STORAGE_PREFIX)) {
|
|
28
|
-
throw new Error(
|
|
29
|
-
"Attack prevented by oidc-spa. You have malicious code running in your system"
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
assert(capturedApis !== undefined);
|
|
34
|
-
|
|
35
|
-
return capturedApis.setItem.call(this, key, value);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
{
|
|
39
|
-
const pd = Object.getOwnPropertyDescriptor(Storage.prototype, "setItem");
|
|
40
|
-
|
|
41
|
-
assert(pd !== undefined);
|
|
42
|
-
|
|
43
|
-
Object.defineProperty(Storage.prototype, "setItem", {
|
|
44
|
-
enumerable: pd.enumerable,
|
|
45
|
-
writable: pd.writable,
|
|
46
|
-
value: setItem_protected
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function getSessionStorageKey(params: { stateUrlParamValue: string }) {
|
|
52
|
-
const { stateUrlParamValue } = params;
|
|
53
|
-
|
|
54
|
-
return `${SESSION_STORAGE_PREFIX}${stateUrlParamValue}`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const ENCRYPTED_AUTH_RESPONSES_PREFIX = "oidc-spa_encrypted_authResponse_";
|
|
58
|
-
|
|
59
|
-
function getIsEncryptedAuthResponse(params: { message: unknown; stateUrlParamValue: string }): boolean {
|
|
60
|
-
const { message, stateUrlParamValue } = params;
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
typeof message === "string" &&
|
|
64
|
-
message.startsWith(`${ENCRYPTED_AUTH_RESPONSES_PREFIX}${stateUrlParamValue}`)
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function getReadyMessage(params: { stateUrlParamValue: string }) {
|
|
69
|
-
const { stateUrlParamValue } = params;
|
|
70
|
-
return `oidc-spa_ready_to_read_publicKey_${stateUrlParamValue}`;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function getIsReadyToReadPublicKeyMessage(params: { message: unknown; stateUrlParamValue: string }) {
|
|
74
|
-
const { message, stateUrlParamValue } = params;
|
|
75
|
-
return message === getReadyMessage({ stateUrlParamValue });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export async function initIframeMessageProtection(params: { stateUrlParamValue: string }) {
|
|
79
|
-
const { stateUrlParamValue } = params;
|
|
80
|
-
|
|
81
|
-
const { publicKey, privateKey } = await generateKeys();
|
|
82
|
-
|
|
83
|
-
const sessionStorageKey = getSessionStorageKey({ stateUrlParamValue });
|
|
84
|
-
|
|
85
|
-
let timer: number | undefined = undefined;
|
|
86
|
-
|
|
87
|
-
function setSessionStoragePublicKey() {
|
|
88
|
-
assert(capturedApis !== undefined);
|
|
89
|
-
|
|
90
|
-
const { setItem } = capturedApis;
|
|
91
|
-
|
|
92
|
-
setItem.call(capturedApis.sessionStorage, sessionStorageKey, publicKey);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function startSessionStoragePublicKeyMaliciousWriteDetection() {
|
|
96
|
-
assert(capturedApis !== undefined);
|
|
97
|
-
|
|
98
|
-
const { alert, setTimeout } = capturedApis;
|
|
99
|
-
|
|
100
|
-
sessionStorage.removeItem(sessionStorageKey);
|
|
101
|
-
|
|
102
|
-
const checkTimeoutCallback = () => {
|
|
103
|
-
const publicKey_inStorage = sessionStorage.getItem(sessionStorageKey);
|
|
104
|
-
|
|
105
|
-
if (publicKey_inStorage !== null && publicKey_inStorage !== publicKey) {
|
|
106
|
-
while (true) {
|
|
107
|
-
alert(
|
|
108
|
-
[
|
|
109
|
-
"⚠️ Security Alert:",
|
|
110
|
-
"oidc-spa detected an attack attempt.",
|
|
111
|
-
"For your safety, please close this tab immediately",
|
|
112
|
-
"and notify the site administrator."
|
|
113
|
-
].join(" ")
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
check();
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
function check() {
|
|
121
|
-
timer = setTimeout(checkTimeoutCallback, 5);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
check();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async function decodeEncryptedAuth(params: {
|
|
128
|
-
encryptedAuthResponse: string;
|
|
129
|
-
}): Promise<{ authResponse: AuthResponse }> {
|
|
130
|
-
const { encryptedAuthResponse } = params;
|
|
131
|
-
|
|
132
|
-
const { message: authResponse_str } = await asymmetricDecrypt({
|
|
133
|
-
encryptedMessage: encryptedAuthResponse.slice(
|
|
134
|
-
ENCRYPTED_AUTH_RESPONSES_PREFIX.length + stateUrlParamValue.length
|
|
135
|
-
),
|
|
136
|
-
privateKey
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const authResponse: AuthResponse = JSON.parse(authResponse_str);
|
|
140
|
-
|
|
141
|
-
return { authResponse };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function clearSessionStoragePublicKey() {
|
|
145
|
-
sessionStorage.removeItem(sessionStorageKey);
|
|
146
|
-
clearTimeout(timer);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
getIsReadyToReadPublicKeyMessage,
|
|
151
|
-
startSessionStoragePublicKeyMaliciousWriteDetection,
|
|
152
|
-
setSessionStoragePublicKey,
|
|
153
|
-
getIsEncryptedAuthResponse,
|
|
154
|
-
decodeEncryptedAuth,
|
|
155
|
-
clearSessionStoragePublicKey
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export async function postEncryptedAuthResponseToParent(params: { authResponse: AuthResponse }) {
|
|
160
|
-
const { authResponse } = params;
|
|
161
|
-
|
|
162
|
-
parent.postMessage(getReadyMessage({ stateUrlParamValue: authResponse.state }), location.origin);
|
|
163
|
-
|
|
164
|
-
await new Promise<void>(resolve => setTimeout(resolve, 2));
|
|
165
|
-
|
|
166
|
-
let publicKey: string | null;
|
|
167
|
-
|
|
168
|
-
{
|
|
169
|
-
let sessionStorageKey = getSessionStorageKey({ stateUrlParamValue: authResponse.state });
|
|
170
|
-
|
|
171
|
-
while ((publicKey = sessionStorage.getItem(sessionStorageKey)) === null) {
|
|
172
|
-
await new Promise<void>(resolve => setTimeout(resolve, 2));
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
await new Promise<void>(resolve => setTimeout(resolve, 7));
|
|
177
|
-
|
|
178
|
-
const { encryptedMessage: encryptedMessage_withoutPrefix } = await asymmetricEncrypt({
|
|
179
|
-
publicKey,
|
|
180
|
-
message: JSON.stringify(authResponse)
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const encryptedMessage = `${ENCRYPTED_AUTH_RESPONSES_PREFIX}${authResponse.state}${encryptedMessage_withoutPrefix}`;
|
|
184
|
-
|
|
185
|
-
parent.postMessage(encryptedMessage, location.origin);
|
|
186
|
-
}
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
type AsymmetricKeys = {
|
|
2
|
-
publicKey: string; // base64-encoded JSON export of CryptoKey
|
|
3
|
-
privateKey: string; // base64-encoded JSON export of CryptoKey
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
const INFO_LABEL = "oidc-spa/tools/asymmetricEncryption";
|
|
7
|
-
|
|
8
|
-
export async function generateKeys(): Promise<AsymmetricKeys> {
|
|
9
|
-
const keyPair = await crypto.subtle.generateKey(
|
|
10
|
-
{
|
|
11
|
-
name: "ECDH",
|
|
12
|
-
namedCurve: "P-256"
|
|
13
|
-
},
|
|
14
|
-
true,
|
|
15
|
-
["deriveKey", "deriveBits"]
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
const publicKeyRaw = await crypto.subtle.exportKey("jwk", keyPair.publicKey);
|
|
19
|
-
const privateKeyRaw = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
publicKey: btoa(JSON.stringify(publicKeyRaw)),
|
|
23
|
-
privateKey: btoa(JSON.stringify(privateKeyRaw))
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function asymmetricEncrypt(params: {
|
|
28
|
-
publicKey: string;
|
|
29
|
-
message: string;
|
|
30
|
-
}): Promise<{ encryptedMessage: string }> {
|
|
31
|
-
const { publicKey, message } = params;
|
|
32
|
-
|
|
33
|
-
const importedPublicKey = await crypto.subtle.importKey(
|
|
34
|
-
"jwk",
|
|
35
|
-
JSON.parse(atob(publicKey)),
|
|
36
|
-
{
|
|
37
|
-
name: "ECDH",
|
|
38
|
-
namedCurve: "P-256"
|
|
39
|
-
},
|
|
40
|
-
false,
|
|
41
|
-
[]
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
const ephemeralKeyPair = await crypto.subtle.generateKey(
|
|
45
|
-
{
|
|
46
|
-
name: "ECDH",
|
|
47
|
-
namedCurve: "P-256"
|
|
48
|
-
},
|
|
49
|
-
true,
|
|
50
|
-
["deriveKey", "deriveBits"]
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
const sharedSecret = await crypto.subtle.deriveBits(
|
|
54
|
-
{
|
|
55
|
-
name: "ECDH",
|
|
56
|
-
public: importedPublicKey
|
|
57
|
-
},
|
|
58
|
-
ephemeralKeyPair.privateKey,
|
|
59
|
-
256
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
63
|
-
const infoBytes = new TextEncoder().encode(INFO_LABEL);
|
|
64
|
-
|
|
65
|
-
const hkdfKey = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, ["deriveKey"]);
|
|
66
|
-
|
|
67
|
-
const derivedKey = await crypto.subtle.deriveKey(
|
|
68
|
-
{
|
|
69
|
-
name: "HKDF",
|
|
70
|
-
hash: "SHA-256",
|
|
71
|
-
salt,
|
|
72
|
-
info: infoBytes
|
|
73
|
-
},
|
|
74
|
-
hkdfKey,
|
|
75
|
-
{ name: "AES-GCM", length: 256 },
|
|
76
|
-
false,
|
|
77
|
-
["encrypt"]
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
81
|
-
const encodedMessage = new TextEncoder().encode(message);
|
|
82
|
-
|
|
83
|
-
const ciphertext = await crypto.subtle.encrypt(
|
|
84
|
-
{
|
|
85
|
-
name: "AES-GCM",
|
|
86
|
-
iv
|
|
87
|
-
},
|
|
88
|
-
derivedKey,
|
|
89
|
-
encodedMessage
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
const ephemeralPubKeyRaw = await crypto.subtle.exportKey("jwk", ephemeralKeyPair.publicKey);
|
|
93
|
-
|
|
94
|
-
const payload = {
|
|
95
|
-
ephemeralPubKey: ephemeralPubKeyRaw,
|
|
96
|
-
iv: Array.from(iv),
|
|
97
|
-
salt: Array.from(salt),
|
|
98
|
-
ciphertext: Array.from(new Uint8Array(ciphertext))
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
encryptedMessage: btoa(JSON.stringify(payload))
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export async function asymmetricDecrypt(params: {
|
|
107
|
-
privateKey: string;
|
|
108
|
-
encryptedMessage: string;
|
|
109
|
-
}): Promise<{ message: string }> {
|
|
110
|
-
const { privateKey, encryptedMessage } = params;
|
|
111
|
-
|
|
112
|
-
const {
|
|
113
|
-
ephemeralPubKey,
|
|
114
|
-
iv,
|
|
115
|
-
salt,
|
|
116
|
-
ciphertext
|
|
117
|
-
}: {
|
|
118
|
-
ephemeralPubKey: JsonWebKey;
|
|
119
|
-
iv: number[];
|
|
120
|
-
salt: number[];
|
|
121
|
-
ciphertext: number[];
|
|
122
|
-
} = JSON.parse(atob(encryptedMessage));
|
|
123
|
-
|
|
124
|
-
const importedPrivateKey = await crypto.subtle.importKey(
|
|
125
|
-
"jwk",
|
|
126
|
-
JSON.parse(atob(privateKey)),
|
|
127
|
-
{
|
|
128
|
-
name: "ECDH",
|
|
129
|
-
namedCurve: "P-256"
|
|
130
|
-
},
|
|
131
|
-
false,
|
|
132
|
-
["deriveKey", "deriveBits"]
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
const importedEphemeralPubKey = await crypto.subtle.importKey(
|
|
136
|
-
"jwk",
|
|
137
|
-
ephemeralPubKey,
|
|
138
|
-
{
|
|
139
|
-
name: "ECDH",
|
|
140
|
-
namedCurve: "P-256"
|
|
141
|
-
},
|
|
142
|
-
false,
|
|
143
|
-
[]
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
const sharedSecret = await crypto.subtle.deriveBits(
|
|
147
|
-
{
|
|
148
|
-
name: "ECDH",
|
|
149
|
-
public: importedEphemeralPubKey
|
|
150
|
-
},
|
|
151
|
-
importedPrivateKey,
|
|
152
|
-
256
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
const infoBytes = new TextEncoder().encode(INFO_LABEL);
|
|
156
|
-
|
|
157
|
-
const hkdfKey = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, ["deriveKey"]);
|
|
158
|
-
|
|
159
|
-
const derivedKey = await crypto.subtle.deriveKey(
|
|
160
|
-
{
|
|
161
|
-
name: "HKDF",
|
|
162
|
-
hash: "SHA-256",
|
|
163
|
-
salt: new Uint8Array(salt),
|
|
164
|
-
info: infoBytes
|
|
165
|
-
},
|
|
166
|
-
hkdfKey,
|
|
167
|
-
{ name: "AES-GCM", length: 256 },
|
|
168
|
-
false,
|
|
169
|
-
["decrypt"]
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
173
|
-
{
|
|
174
|
-
name: "AES-GCM",
|
|
175
|
-
iv: new Uint8Array(iv)
|
|
176
|
-
},
|
|
177
|
-
derivedKey,
|
|
178
|
-
new Uint8Array(ciphertext)
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
message: new TextDecoder().decode(decryptedBuffer)
|
|
183
|
-
};
|
|
184
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
type AsymmetricKeys = {
|
|
2
|
-
publicKey: string;
|
|
3
|
-
privateKey: string;
|
|
4
|
-
};
|
|
5
|
-
export declare function generateKeys(): Promise<AsymmetricKeys>;
|
|
6
|
-
export declare function asymmetricEncrypt(params: {
|
|
7
|
-
publicKey: string;
|
|
8
|
-
message: string;
|
|
9
|
-
}): Promise<{
|
|
10
|
-
encryptedMessage: string;
|
|
11
|
-
}>;
|
|
12
|
-
export declare function asymmetricDecrypt(params: {
|
|
13
|
-
privateKey: string;
|
|
14
|
-
encryptedMessage: string;
|
|
15
|
-
}): Promise<{
|
|
16
|
-
message: string;
|
|
17
|
-
}>;
|
|
18
|
-
export {};
|