-
Notifications
You must be signed in to change notification settings - Fork 78
Expand file tree
/
Copy pathTokenConnector.ts
More file actions
80 lines (69 loc) · 2.41 KB
/
Copy pathTokenConnector.ts
File metadata and controls
80 lines (69 loc) · 2.41 KB
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
import { AbstractPowerSyncDatabase, PowerSyncBackendConnector } from '@powersync/web';
import { connect } from './ConnectionManager';
import { LoginDetailsFormValues } from '@/components/widgets/LoginDetailsWidget';
export interface Credentials {
token: string;
endpoint: string;
}
export class TokenConnector implements PowerSyncBackendConnector {
async fetchCredentials() {
const value = localStorage.getItem('powersync_credentials');
if (value == null) {
return null;
}
return JSON.parse(value);
}
async uploadData(database: AbstractPowerSyncDatabase) {
// Discard any data
const tx = await database.getNextCrudTransaction();
await tx?.complete();
}
async signIn(credentials: LoginDetailsFormValues) {
validateSecureContext(credentials.endpoint);
checkJWT(credentials.token);
try {
localStorage.setItem('powersync_credentials', JSON.stringify(credentials));
localStorage.setItem('preferred_client_implementation', credentials.clientImplementation);
await connect();
} catch (e) {
this.clearCredentials();
throw e;
}
}
hasCredentials() {
return localStorage.getItem('powersync_credentials') != null;
}
clearCredentials() {
localStorage.removeItem('powersync_credentials');
localStorage.removeItem('preferred_client_implementation');
}
}
function validateSecureContext(url: string) {
if (!location.href.startsWith('https:')) {
return;
}
const parsedUrl = new URL(url);
const secure =
parsedUrl.protocol === 'https:' ||
parsedUrl.hostname === 'localhost' ||
parsedUrl.hostname === '127.0.0.1' ||
parsedUrl.hostname === '::1';
if (!secure) {
throw new Error(`Cannot connect to http endpoints from the hosted diagnostics app.
Run either the PowerSync endpoint on http://localhost, or the diagnostics app on http://localhost.`);
}
}
function checkJWT(token: string) {
// Split the token into parts by "."
const parts = token.split('.');
// Check that it has exactly three parts (header, payload, signature)
if (parts.length !== 3) {
throw new Error(`Token must be a JWT: Expected 3 parts, got ${parts.length}`);
}
// Check that each part is base64 or base64url encoded
const base64UrlRegex = /^[A-Za-z0-9-_]+$/;
const isBase64 = parts.every((part) => base64UrlRegex.test(part));
if (!isBase64) {
throw new Error(`Token must be a JWT: Not all parts are base64 encoded`);
}
}