/
AppleProvider.ts
115 lines (103 loc) · 3.22 KB
/
AppleProvider.ts
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
112
113
114
115
import {
AuthMethod,
BaseProviderOptions,
OAuthProviderOptions,
} from '@lit-protocol/types';
import { AuthMethodType } from '@lit-protocol/constants';
import {
prepareLoginUrl,
parseLoginParams,
getStateParam,
decode,
} from '../utils';
import { BaseProvider } from './BaseProvider';
import { ethers } from 'ethers';
import * as jose from 'jose';
export default class AppleProvider extends BaseProvider {
/**
* The redirect URI that Lit's login server should send the user back to
*/
public redirectUri: string;
constructor(options: BaseProviderOptions & OAuthProviderOptions) {
super(options);
this.redirectUri = options.redirectUri || window.location.origin;
}
/**
* Redirect user to the Lit's Apple login page
*
* @returns {Promise<void>} - Redirects user to Lit login page
*/
public async signIn(): Promise<void> {
// Get login url
const loginUrl = await prepareLoginUrl('apple', this.redirectUri);
// Redirect to login url
window.location.assign(loginUrl);
}
/**
* Validate the URL parameters returned from Lit's login server and return the authentication data
*
* @returns {Promise<AuthMethod>} - Auth method object that contains OAuth token
*/
public async authenticate(): Promise<AuthMethod> {
// Check if current url matches redirect uri
if (!window.location.href.startsWith(this.redirectUri)) {
throw new Error(
`Current url "${window.location.href}" does not match provided redirect uri "${this.redirectUri}"`
);
}
// Check url for params
const { provider, idToken, state, error } = parseLoginParams(
window.location.search
);
// Check if there's an error
if (error) {
throw new Error(error);
}
// Check if provider is Apple
if (!provider || provider !== 'apple') {
throw new Error(
`OAuth provider "${provider}" passed in redirect callback URL does not match "apple"`
);
}
// Check if state param matches
if (!state || decode(decodeURIComponent(state)) !== getStateParam()) {
throw new Error(
`Invalid state parameter "${state}" passed in redirect callback URL`
);
}
// Clear params from url
window.history.replaceState(
null,
window.document.title,
window.location.pathname
);
// Check if id token is present in url
if (!idToken) {
throw new Error(
`Missing ID token in redirect callback URL for Apple OAuth"`
);
}
const authMethod = {
authMethodType: AuthMethodType.AppleJwt,
accessToken: idToken,
};
return authMethod;
}
/**
* Get auth method id that can be used to look up and interact with
* PKPs associated with the given auth method
*
* @param {AuthMethod} authMethod - Auth method object
*
* @returns {Promise<string>} - Auth method id
*/
public async getAuthMethodId(authMethod: AuthMethod): Promise<string> {
const tokenPayload = jose.decodeJwt(authMethod.accessToken);
const userId: string = tokenPayload['sub'] as string;
const audience: string = tokenPayload['aud'] as string;
const authMethodId = ethers.utils.keccak256(
ethers.utils.toUtf8Bytes(`${userId}:${audience}`)
);
return authMethodId;
}
}