@@ -22,6 +22,8 @@ export class TokenConnector implements PowerSyncBackendConnector {
2222 }
2323
2424 async signIn ( credentials : Credentials ) {
25+ validateSecureContext ( credentials . endpoint ) ;
26+ checkJWT ( credentials . token ) ;
2527 try {
2628 localStorage . setItem ( 'powersync_credentials' , JSON . stringify ( credentials ) ) ;
2729 await connect ( ) ;
@@ -39,3 +41,37 @@ export class TokenConnector implements PowerSyncBackendConnector {
3941 localStorage . removeItem ( 'powersync_credentials' ) ;
4042 }
4143}
44+
45+ function validateSecureContext ( url : string ) {
46+ if ( ! location . href . startsWith ( 'https:' ) ) {
47+ return ;
48+ }
49+ const parsedUrl = new URL ( url ) ;
50+ const secure =
51+ parsedUrl . protocol === 'https:' ||
52+ parsedUrl . hostname === 'localhost' ||
53+ parsedUrl . hostname === '127.0.0.1' ||
54+ parsedUrl . hostname === '::1' ;
55+ if ( ! secure ) {
56+ throw new Error ( `Cannot connect to http endpoints from the hosted diagnostics app.
57+ Run either the PowerSync endpoint on http://localhost, or the diagnostics app on http://localhost.` ) ;
58+ }
59+ }
60+
61+ function checkJWT ( token : string ) {
62+ // Split the token into parts by "."
63+ const parts = token . split ( '.' ) ;
64+
65+ // Check that it has exactly three parts (header, payload, signature)
66+ if ( parts . length !== 3 ) {
67+ throw new Error ( `Token must be a JWT: Expected 3 parts, got ${ parts . length } ` ) ;
68+ }
69+
70+ // Check that each part is base64 or base64url encoded
71+ const base64UrlRegex = / ^ [ A - Z a - z 0 - 9 - _ ] + $ / ;
72+
73+ const isBase64 = parts . every ( ( part ) => base64UrlRegex . test ( part ) ) ;
74+ if ( ! isBase64 ) {
75+ throw new Error ( `Token must be a JWT: Not all parts are base64 encoded` ) ;
76+ }
77+ }
0 commit comments