-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
/
Copy pathapple.js
111 lines (93 loc) · 2.75 KB
/
apple.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Apple SignIn Auth
// https://developer.apple.com/documentation/signinwithapplerestapi
const Parse = require('parse/node').Parse;
const jwksClient = require('jwks-rsa');
const util = require('util');
const jwt = require('jsonwebtoken');
const TOKEN_ISSUER = 'https://appleid.apple.com';
const getAppleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
const client = jwksClient({
jwksUri: `${TOKEN_ISSUER}/auth/keys`,
cache: true,
cacheMaxEntries,
cacheMaxAge,
});
const asyncGetSigningKeyFunction = util.promisify(client.getSigningKey);
let key;
try {
key = await asyncGetSigningKeyFunction(keyId);
} catch (error) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
`Unable to find matching key for Key ID: ${keyId}`
);
}
return key;
};
const getHeaderFromToken = token => {
const decodedToken = jwt.decode(token, { complete: true });
if (!decodedToken) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
`provided token does not decode as JWT`
);
}
return decodedToken.header;
};
const verifyIdToken = async (
{ token, id },
{ clientId, cacheMaxEntries, cacheMaxAge }
) => {
if (!token) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
`id token is invalid for this user.`
);
}
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
const ONE_HOUR_IN_MS = 3600000;
let jwtClaims;
cacheMaxAge = cacheMaxAge || ONE_HOUR_IN_MS;
cacheMaxEntries = cacheMaxEntries || 5;
const appleKey = await getAppleKeyByKeyId(
keyId,
cacheMaxEntries,
cacheMaxAge
);
const signingKey = appleKey.publicKey || appleKey.rsaPublicKey;
try {
jwtClaims = jwt.verify(token, signingKey, {
algorithms: algorithm,
// the audience can be checked against a string, a regular expression or a list of strings and/or regular expressions.
audience: clientId,
});
} catch (exception) {
const message = exception.message;
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${message}`);
}
if (jwtClaims.iss !== TOKEN_ISSUER) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
`id token not issued by correct OpenID provider - expected: ${TOKEN_ISSUER} | from: ${jwtClaims.iss}`
);
}
if (jwtClaims.sub !== id) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
`auth data is invalid for this user.`
);
}
return jwtClaims;
};
// Returns a promise that fulfills if this id token is valid
function validateAuthData(authData, options = {}) {
return verifyIdToken(authData, options);
}
// Returns a promise that fulfills if this app id is valid.
function validateAppId() {
return Promise.resolve();
}
module.exports = {
validateAppId,
validateAuthData,
};