@sveltebase/auth 1.0.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 +395 -0
- package/dist/client/auth.svelte.d.ts +47 -0
- package/dist/client/auth.svelte.d.ts.map +1 -0
- package/dist/client/auth.svelte.js +130 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +1 -0
- package/dist/google/GoogleLogin.svelte +83 -0
- package/dist/google/GoogleLogin.svelte.d.ts +22 -0
- package/dist/google/GoogleLogin.svelte.d.ts.map +1 -0
- package/dist/google/GoogleOAuthProvider.svelte +39 -0
- package/dist/google/GoogleOAuthProvider.svelte.d.ts +10 -0
- package/dist/google/GoogleOAuthProvider.svelte.d.ts.map +1 -0
- package/dist/google/GoogleOneTapLogin.svelte +52 -0
- package/dist/google/GoogleOneTapLogin.svelte.d.ts +14 -0
- package/dist/google/GoogleOneTapLogin.svelte.d.ts.map +1 -0
- package/dist/google/context.svelte.d.ts +11 -0
- package/dist/google/context.svelte.d.ts.map +1 -0
- package/dist/google/context.svelte.js +23 -0
- package/dist/google/google.svelte.d.ts +20 -0
- package/dist/google/google.svelte.d.ts.map +1 -0
- package/dist/google/google.svelte.js +109 -0
- package/dist/google/index.d.ts +9 -0
- package/dist/google/index.d.ts.map +1 -0
- package/dist/google/index.js +8 -0
- package/dist/google/loader.d.ts +2 -0
- package/dist/google/loader.d.ts.map +1 -0
- package/dist/google/loader.js +27 -0
- package/dist/google/types.d.ts +117 -0
- package/dist/google/types.d.ts.map +1 -0
- package/dist/google/types.js +1 -0
- package/dist/google/verifier.d.ts +17 -0
- package/dist/google/verifier.d.ts.map +1 -0
- package/dist/google/verifier.js +64 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +194 -0
- package/dist/server/index.d.ts +28 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +70 -0
- package/package.json +46 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleLogin.svelte.d.ts","sourceRoot":"","sources":["../../src/google/GoogleLogin.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGpE,UAAU,KAAK;IACb,SAAS,EAAE,CAAC,kBAAkB,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC5D,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,wBAAwB,CAAC,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACtE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,GAAG,aAAa,GAAG,cAAc,CAAC;IACnD,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,IAAI,CAAC,EAAE,aAAa,GAAG,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,eAAe,GAAG,oBAAoB,CAAC;IACpG,KAAK,CAAC,EAAE,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACrD,cAAc,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAsEH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { GoogleOAuthState, setGoogleOAuthContext } from './context.svelte.js';
|
|
3
|
+
import { loadGoogleScript } from './loader';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
clientId: string;
|
|
7
|
+
onScriptLoadSuccess?: () => void;
|
|
8
|
+
onScriptLoadError?: (err: Error) => void;
|
|
9
|
+
children?: import('svelte').Snippet;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
clientId,
|
|
14
|
+
onScriptLoadSuccess,
|
|
15
|
+
onScriptLoadError,
|
|
16
|
+
children
|
|
17
|
+
}: Props = $props();
|
|
18
|
+
|
|
19
|
+
// Create and set the reactive context state using a getter closure
|
|
20
|
+
const state = new GoogleOAuthState(() => clientId);
|
|
21
|
+
setGoogleOAuthContext(state);
|
|
22
|
+
|
|
23
|
+
// Load the Google script on the client side
|
|
24
|
+
$effect(() => {
|
|
25
|
+
loadGoogleScript()
|
|
26
|
+
.then(() => {
|
|
27
|
+
state.isLoaded = true;
|
|
28
|
+
onScriptLoadSuccess?.();
|
|
29
|
+
})
|
|
30
|
+
.catch((err) => {
|
|
31
|
+
state.error = err;
|
|
32
|
+
onScriptLoadError?.(err);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
{#if children}
|
|
38
|
+
{@render children()}
|
|
39
|
+
{/if}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
clientId: string;
|
|
3
|
+
onScriptLoadSuccess?: () => void;
|
|
4
|
+
onScriptLoadError?: (err: Error) => void;
|
|
5
|
+
children?: import('svelte').Snippet;
|
|
6
|
+
}
|
|
7
|
+
declare const GoogleOAuthProvider: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type GoogleOAuthProvider = ReturnType<typeof GoogleOAuthProvider>;
|
|
9
|
+
export default GoogleOAuthProvider;
|
|
10
|
+
//# sourceMappingURL=GoogleOAuthProvider.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleOAuthProvider.svelte.d.ts","sourceRoot":"","sources":["../../src/google/GoogleOAuthProvider.svelte.ts"],"names":[],"mappings":"AAOE,UAAU,KAAK;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;CACrC;AAqCH,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getGoogleOAuthContext } from './context.svelte.js';
|
|
3
|
+
import type { CredentialResponse, MomentNotification } from './types';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
onSuccess: (credentialResponse: CredentialResponse) => void;
|
|
7
|
+
onError?: () => void;
|
|
8
|
+
promptMomentNotification?: (notification: MomentNotification) => void;
|
|
9
|
+
auto_select?: boolean;
|
|
10
|
+
cancel_on_tap_outside?: boolean;
|
|
11
|
+
nonce?: string;
|
|
12
|
+
hosted_domain?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
onSuccess,
|
|
17
|
+
onError,
|
|
18
|
+
promptMomentNotification,
|
|
19
|
+
auto_select = false,
|
|
20
|
+
cancel_on_tap_outside = true,
|
|
21
|
+
nonce,
|
|
22
|
+
hosted_domain
|
|
23
|
+
}: Props = $props();
|
|
24
|
+
|
|
25
|
+
const ctx = getGoogleOAuthContext();
|
|
26
|
+
|
|
27
|
+
$effect(() => {
|
|
28
|
+
if (!ctx.isLoaded) return;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
window.google.accounts.id.initialize({
|
|
32
|
+
client_id: ctx.clientId,
|
|
33
|
+
callback: (response) => {
|
|
34
|
+
if (response.credential) {
|
|
35
|
+
onSuccess(response);
|
|
36
|
+
} else {
|
|
37
|
+
onError?.();
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
auto_select,
|
|
41
|
+
cancel_on_tap_outside,
|
|
42
|
+
nonce,
|
|
43
|
+
hosted_domain
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
window.google.accounts.id.prompt(promptMomentNotification);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
console.error('Error initializing Google One Tap:', err);
|
|
49
|
+
onError?.();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
</script>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CredentialResponse, MomentNotification } from './types';
|
|
2
|
+
interface Props {
|
|
3
|
+
onSuccess: (credentialResponse: CredentialResponse) => void;
|
|
4
|
+
onError?: () => void;
|
|
5
|
+
promptMomentNotification?: (notification: MomentNotification) => void;
|
|
6
|
+
auto_select?: boolean;
|
|
7
|
+
cancel_on_tap_outside?: boolean;
|
|
8
|
+
nonce?: string;
|
|
9
|
+
hosted_domain?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const GoogleOneTapLogin: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type GoogleOneTapLogin = ReturnType<typeof GoogleOneTapLogin>;
|
|
13
|
+
export default GoogleOneTapLogin;
|
|
14
|
+
//# sourceMappingURL=GoogleOneTapLogin.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleOneTapLogin.svelte.d.ts","sourceRoot":"","sources":["../../src/google/GoogleOneTapLogin.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGpE,UAAU,KAAK;IACb,SAAS,EAAE,CAAC,kBAAkB,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC5D,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,wBAAwB,CAAC,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACtE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA+CH,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const GOOGLE_OAUTH_CONTEXT_KEY: unique symbol;
|
|
2
|
+
export declare class GoogleOAuthState {
|
|
3
|
+
#private;
|
|
4
|
+
isLoaded: boolean;
|
|
5
|
+
error: Error | null;
|
|
6
|
+
get clientId(): string;
|
|
7
|
+
constructor(clientIdGetter: () => string);
|
|
8
|
+
}
|
|
9
|
+
export declare function setGoogleOAuthContext(state: GoogleOAuthState): void;
|
|
10
|
+
export declare function getGoogleOAuthContext(): GoogleOAuthState;
|
|
11
|
+
//# sourceMappingURL=context.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.svelte.d.ts","sourceRoot":"","sources":["../../src/google/context.svelte.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,wBAAwB,eAAiC,CAAC;AAEvE,qBAAa,gBAAgB;;IAE3B,QAAQ,UAA0B;IAClC,KAAK,eAA8B;IAEnC,IAAI,QAAQ,IAAI,MAAM,CAErB;gBAEW,cAAc,EAAE,MAAM,MAAM;CAGzC;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAEnE;AAED,wBAAgB,qBAAqB,IAAI,gBAAgB,CAMxD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
export const GOOGLE_OAUTH_CONTEXT_KEY = Symbol('google-oauth-context');
|
|
3
|
+
export class GoogleOAuthState {
|
|
4
|
+
#clientIdGetter;
|
|
5
|
+
isLoaded = $state(false);
|
|
6
|
+
error = $state(null);
|
|
7
|
+
get clientId() {
|
|
8
|
+
return this.#clientIdGetter();
|
|
9
|
+
}
|
|
10
|
+
constructor(clientIdGetter) {
|
|
11
|
+
this.#clientIdGetter = clientIdGetter;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function setGoogleOAuthContext(state) {
|
|
15
|
+
setContext(GOOGLE_OAUTH_CONTEXT_KEY, state);
|
|
16
|
+
}
|
|
17
|
+
export function getGoogleOAuthContext() {
|
|
18
|
+
const context = getContext(GOOGLE_OAUTH_CONTEXT_KEY);
|
|
19
|
+
if (!context) {
|
|
20
|
+
throw new Error('Google OAuth Context not found. Make sure your component is wrapped in <GoogleOAuthProvider>.');
|
|
21
|
+
}
|
|
22
|
+
return context;
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { GoogleLoginOptions, GoogleData } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Triggers the Google Identity Services OAuth2 flow (Token or Code client).
|
|
4
|
+
* Returns an object containing the trigger function `login`, and the reactive state properties `loading` and `error`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createGoogleLogin(options?: GoogleLoginOptions): {
|
|
7
|
+
login: (overrideOptions?: any) => void;
|
|
8
|
+
readonly loading: boolean;
|
|
9
|
+
readonly error: Error | null;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Logs out the user from Google Identity Services session (disables auto-select).
|
|
13
|
+
*/
|
|
14
|
+
export declare function googleLogout(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Decodes the credential JWT returned by Google Identity Services.
|
|
17
|
+
* Returns a typed `GoogleData` object.
|
|
18
|
+
*/
|
|
19
|
+
export declare function decodeCredentials<T = GoogleData>(credential: string): T;
|
|
20
|
+
//# sourceMappingURL=google.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google.svelte.d.ts","sourceRoot":"","sources":["../../src/google/google.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEjE;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB;8BAM/B,GAAG;;;EAgErC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAInC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,CAyBvE"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { getGoogleOAuthContext } from './context.svelte.js';
|
|
2
|
+
/**
|
|
3
|
+
* Triggers the Google Identity Services OAuth2 flow (Token or Code client).
|
|
4
|
+
* Returns an object containing the trigger function `login`, and the reactive state properties `loading` and `error`.
|
|
5
|
+
*/
|
|
6
|
+
export function createGoogleLogin(options = {}) {
|
|
7
|
+
const ctx = getGoogleOAuthContext();
|
|
8
|
+
let loading = $state(false);
|
|
9
|
+
let error = $state(null);
|
|
10
|
+
const login = (overrideOptions) => {
|
|
11
|
+
if (!ctx.isLoaded) {
|
|
12
|
+
console.warn('Google Identity Services script is not loaded yet');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
loading = true;
|
|
16
|
+
error = null;
|
|
17
|
+
const flow = options.flow ?? 'implicit';
|
|
18
|
+
const scope = options.scope ?? '';
|
|
19
|
+
const overrideScope = options.overrideScope ?? false;
|
|
20
|
+
const prompt = options.prompt;
|
|
21
|
+
const login_hint = options.login_hint;
|
|
22
|
+
const state = options.state;
|
|
23
|
+
const ux_mode = options.ux_mode ?? 'popup';
|
|
24
|
+
const redirect_uri = options.redirect_uri;
|
|
25
|
+
const clientMethod = flow === 'implicit' ? 'initTokenClient' : 'initCodeClient';
|
|
26
|
+
const finalScope = overrideScope ? scope : `openid profile email ${scope}`.trim().replace(/\s+/g, ' ');
|
|
27
|
+
try {
|
|
28
|
+
const client = window.google.accounts.oauth2[clientMethod]({
|
|
29
|
+
client_id: ctx.clientId,
|
|
30
|
+
scope: finalScope,
|
|
31
|
+
prompt,
|
|
32
|
+
login_hint,
|
|
33
|
+
state,
|
|
34
|
+
ux_mode,
|
|
35
|
+
redirect_uri,
|
|
36
|
+
callback: (response) => {
|
|
37
|
+
loading = false;
|
|
38
|
+
if (response.error) {
|
|
39
|
+
error = new Error(response.error_description || response.error);
|
|
40
|
+
options.onError?.(response);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
options.onSuccess?.(response);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
error_callback: (nonOAuthError) => {
|
|
47
|
+
loading = false;
|
|
48
|
+
error = new Error(nonOAuthError.type || 'Non-OAuth Error');
|
|
49
|
+
options.onNonOAuthError?.(nonOAuthError);
|
|
50
|
+
options.onError?.(nonOAuthError);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
if (flow === 'implicit') {
|
|
54
|
+
client.requestAccessToken(overrideOptions);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
client.requestCode();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
loading = false;
|
|
62
|
+
error = err;
|
|
63
|
+
options.onError?.(err);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
login,
|
|
68
|
+
get loading() { return loading; },
|
|
69
|
+
get error() { return error; }
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Logs out the user from Google Identity Services session (disables auto-select).
|
|
74
|
+
*/
|
|
75
|
+
export function googleLogout() {
|
|
76
|
+
if (typeof window !== 'undefined' && window.google?.accounts?.id) {
|
|
77
|
+
window.google.accounts.id.disableAutoSelect();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Decodes the credential JWT returned by Google Identity Services.
|
|
82
|
+
* Returns a typed `GoogleData` object.
|
|
83
|
+
*/
|
|
84
|
+
export function decodeCredentials(credential) {
|
|
85
|
+
const parts = credential.split('.');
|
|
86
|
+
if (parts.length !== 3) {
|
|
87
|
+
throw new Error('Invalid token status: JWT must have 3 parts');
|
|
88
|
+
}
|
|
89
|
+
const payload = parts[1];
|
|
90
|
+
const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');
|
|
91
|
+
const pad = base64.length % 4;
|
|
92
|
+
const paddedBase64 = pad ? base64 + '='.repeat(4 - pad) : base64;
|
|
93
|
+
let decodedStr;
|
|
94
|
+
if (typeof atob === 'function') {
|
|
95
|
+
decodedStr = atob(paddedBase64);
|
|
96
|
+
}
|
|
97
|
+
else if (typeof Buffer !== 'undefined') {
|
|
98
|
+
decodedStr = Buffer.from(paddedBase64, 'base64').toString('binary');
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
throw new Error('Environment not supported: missing base64 decoding helper');
|
|
102
|
+
}
|
|
103
|
+
const bytes = new Uint8Array(decodedStr.length);
|
|
104
|
+
for (let i = 0; i < decodedStr.length; i++) {
|
|
105
|
+
bytes[i] = decodedStr.charCodeAt(i);
|
|
106
|
+
}
|
|
107
|
+
const textDecoder = new TextDecoder('utf-8');
|
|
108
|
+
return JSON.parse(textDecoder.decode(bytes));
|
|
109
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './google.svelte.js';
|
|
2
|
+
export { loadGoogleScript } from './loader.js';
|
|
3
|
+
export { GoogleOAuthState, setGoogleOAuthContext, getGoogleOAuthContext, GOOGLE_OAUTH_CONTEXT_KEY } from './context.svelte.js';
|
|
4
|
+
export { default as GoogleOAuthProvider } from './GoogleOAuthProvider.svelte';
|
|
5
|
+
export { default as GoogleLogin } from './GoogleLogin.svelte';
|
|
6
|
+
export { default as GoogleOneTapLogin } from './GoogleOneTapLogin.svelte';
|
|
7
|
+
export { verifyIdToken } from './verifier.js';
|
|
8
|
+
export * from './types.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/google/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE1E,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './google.svelte.js';
|
|
2
|
+
export { loadGoogleScript } from './loader.js';
|
|
3
|
+
export { GoogleOAuthState, setGoogleOAuthContext, getGoogleOAuthContext, GOOGLE_OAUTH_CONTEXT_KEY } from './context.svelte.js';
|
|
4
|
+
export { default as GoogleOAuthProvider } from './GoogleOAuthProvider.svelte';
|
|
5
|
+
export { default as GoogleLogin } from './GoogleLogin.svelte';
|
|
6
|
+
export { default as GoogleOneTapLogin } from './GoogleOneTapLogin.svelte';
|
|
7
|
+
export { verifyIdToken } from './verifier.js';
|
|
8
|
+
export * from './types.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/google/loader.ts"],"names":[],"mappings":"AAEA,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CA6BhD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
let scriptLoadingPromise = null;
|
|
2
|
+
export function loadGoogleScript() {
|
|
3
|
+
if (typeof window === 'undefined') {
|
|
4
|
+
return Promise.reject(new Error('Browser environment required to load Google Identity Services script'));
|
|
5
|
+
}
|
|
6
|
+
if (window.google?.accounts?.id) {
|
|
7
|
+
return Promise.resolve();
|
|
8
|
+
}
|
|
9
|
+
if (scriptLoadingPromise) {
|
|
10
|
+
return scriptLoadingPromise;
|
|
11
|
+
}
|
|
12
|
+
scriptLoadingPromise = new Promise((resolve, reject) => {
|
|
13
|
+
const script = document.createElement('script');
|
|
14
|
+
script.src = 'https://accounts.google.com/gsi/client';
|
|
15
|
+
script.async = true;
|
|
16
|
+
script.defer = true;
|
|
17
|
+
script.onload = () => {
|
|
18
|
+
resolve();
|
|
19
|
+
};
|
|
20
|
+
script.onerror = (err) => {
|
|
21
|
+
scriptLoadingPromise = null;
|
|
22
|
+
reject(new Error('Failed to load Google Identity Services script'));
|
|
23
|
+
};
|
|
24
|
+
document.head.appendChild(script);
|
|
25
|
+
});
|
|
26
|
+
return scriptLoadingPromise;
|
|
27
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
export interface GoogleData {
|
|
2
|
+
iss: string;
|
|
3
|
+
azp: string;
|
|
4
|
+
aud: string;
|
|
5
|
+
sub: string;
|
|
6
|
+
email: string;
|
|
7
|
+
email_verified: boolean;
|
|
8
|
+
nonce?: string;
|
|
9
|
+
nbf?: number;
|
|
10
|
+
name: string;
|
|
11
|
+
picture?: string;
|
|
12
|
+
given_name?: string;
|
|
13
|
+
family_name?: string;
|
|
14
|
+
iat: number;
|
|
15
|
+
exp: number;
|
|
16
|
+
jti?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface CredentialResponse {
|
|
19
|
+
credential?: string;
|
|
20
|
+
select_by?: 'auto' | 'user' | 'user_1tap' | 'user_2tap' | 'btn_confirm' | 'btn_confirm_1tap' | 'btn_confirm_2tap';
|
|
21
|
+
}
|
|
22
|
+
export interface IdConfiguration {
|
|
23
|
+
client_id: string;
|
|
24
|
+
callback?: (response: CredentialResponse) => void;
|
|
25
|
+
auto_select?: boolean;
|
|
26
|
+
callback_parent_id?: string;
|
|
27
|
+
cancel_on_tap_outside?: boolean;
|
|
28
|
+
prompt_parent_id?: string;
|
|
29
|
+
nonce?: string;
|
|
30
|
+
context?: 'signin' | 'signup' | 'use';
|
|
31
|
+
state_cookie_domain?: string;
|
|
32
|
+
ux_mode?: 'popup' | 'redirect';
|
|
33
|
+
allowed_parent_origin?: string | string[];
|
|
34
|
+
intermediate_iframe_close_callback?: () => void;
|
|
35
|
+
itp_support?: boolean;
|
|
36
|
+
login_hint?: string;
|
|
37
|
+
hd?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface TokenResponse {
|
|
40
|
+
access_token: string;
|
|
41
|
+
expires_in: string;
|
|
42
|
+
hd?: string;
|
|
43
|
+
prompt: string;
|
|
44
|
+
token_type: string;
|
|
45
|
+
scope: string;
|
|
46
|
+
state?: string;
|
|
47
|
+
error?: string;
|
|
48
|
+
error_description?: string;
|
|
49
|
+
error_uri?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface TokenClientConfig {
|
|
52
|
+
client_id: string;
|
|
53
|
+
scope: string;
|
|
54
|
+
callback: (response: TokenResponse) => void;
|
|
55
|
+
error_callback?: (error: NonOAuthError) => void;
|
|
56
|
+
prompt?: 'none' | 'consent' | 'select_account';
|
|
57
|
+
enable_serial_consent?: boolean;
|
|
58
|
+
hint?: string;
|
|
59
|
+
login_hint?: string;
|
|
60
|
+
state?: string;
|
|
61
|
+
include_granted_scopes?: boolean;
|
|
62
|
+
}
|
|
63
|
+
export interface CodeResponse {
|
|
64
|
+
code: string;
|
|
65
|
+
scope: string;
|
|
66
|
+
state?: string;
|
|
67
|
+
error?: string;
|
|
68
|
+
error_description?: string;
|
|
69
|
+
error_uri?: string;
|
|
70
|
+
}
|
|
71
|
+
export interface CodeClientConfig {
|
|
72
|
+
client_id: string;
|
|
73
|
+
scope: string;
|
|
74
|
+
callback: (response: CodeResponse) => void;
|
|
75
|
+
error_callback?: (error: NonOAuthError) => void;
|
|
76
|
+
ux_mode?: 'popup' | 'redirect';
|
|
77
|
+
redirect_uri?: string;
|
|
78
|
+
prompt?: 'none' | 'consent' | 'select_account';
|
|
79
|
+
enable_serial_consent?: boolean;
|
|
80
|
+
hint?: string;
|
|
81
|
+
login_hint?: string;
|
|
82
|
+
state?: string;
|
|
83
|
+
include_granted_scopes?: boolean;
|
|
84
|
+
}
|
|
85
|
+
export interface NonOAuthError {
|
|
86
|
+
type: 'popup_closed' | 'popup_blocked_by_browser' | 'unknown';
|
|
87
|
+
}
|
|
88
|
+
export interface GoogleLoginOptions {
|
|
89
|
+
flow?: 'implicit' | 'auth-code';
|
|
90
|
+
scope?: string;
|
|
91
|
+
prompt?: 'none' | 'consent' | 'select_account';
|
|
92
|
+
login_hint?: string;
|
|
93
|
+
state?: string;
|
|
94
|
+
overrideScope?: boolean;
|
|
95
|
+
ux_mode?: 'popup' | 'redirect';
|
|
96
|
+
redirect_uri?: string;
|
|
97
|
+
onSuccess?: (response: any) => void;
|
|
98
|
+
onError?: (error: any) => void;
|
|
99
|
+
onNonOAuthError?: (error: NonOAuthError) => void;
|
|
100
|
+
}
|
|
101
|
+
export interface OverridableTokenClientConfig {
|
|
102
|
+
prompt?: 'none' | 'consent' | 'select_account';
|
|
103
|
+
login_hint?: string;
|
|
104
|
+
state?: string;
|
|
105
|
+
}
|
|
106
|
+
export interface MomentNotification {
|
|
107
|
+
isDisplayMoment: () => boolean;
|
|
108
|
+
isDisplayed: () => boolean;
|
|
109
|
+
isNotDisplayed: () => boolean;
|
|
110
|
+
getNotDisplayedReason: () => 'browser_not_supported' | 'unknown_reason' | 'opt_out' | 'user_cancel' | 'suppressed_by_user' | 'unregistered_origin' | 'unknown_sharing_id' | 'third_party_cookies_disabled' | 'iss_missing' | 'client_id_missing' | 'credential_disabled' | 'secure_context_required' | 'hd_required';
|
|
111
|
+
isSkippedMoment: () => boolean;
|
|
112
|
+
getSkippedReason: () => 'auto_cancel' | 'user_cancel' | 'tap_outside' | 'iss_missing';
|
|
113
|
+
isDismissedMoment: () => boolean;
|
|
114
|
+
getDismissedReason: () => 'credential_returned' | 'cancel_called' | 'flow_restarted' | 'user_cancel' | 'tap_outside';
|
|
115
|
+
getMomentType: () => 'display' | 'skipped' | 'dismissed';
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/google/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { GoogleData } from "./types.js";
|
|
2
|
+
export interface VerifyIdTokenOptions {
|
|
3
|
+
/**
|
|
4
|
+
* The credential string (JWT token) returned by Google Identity Services.
|
|
5
|
+
*/
|
|
6
|
+
credential: string;
|
|
7
|
+
/**
|
|
8
|
+
* Your Google OAuth Client ID to verify the audience.
|
|
9
|
+
*/
|
|
10
|
+
clientId: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Cryptographically verifies a Google ID token (credential) signature
|
|
14
|
+
* and claims using the Web Crypto API. Does not require a Client Secret.
|
|
15
|
+
*/
|
|
16
|
+
export declare function verifyIdToken(options: VerifyIdTokenOptions): Promise<GoogleData>;
|
|
17
|
+
//# sourceMappingURL=verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../src/google/verifier.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC,CAgFtF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { decodeCredentials } from "./google.svelte.js";
|
|
2
|
+
/**
|
|
3
|
+
* Cryptographically verifies a Google ID token (credential) signature
|
|
4
|
+
* and claims using the Web Crypto API. Does not require a Client Secret.
|
|
5
|
+
*/
|
|
6
|
+
export async function verifyIdToken(options) {
|
|
7
|
+
const { credential, clientId } = options;
|
|
8
|
+
const parts = credential.split(".");
|
|
9
|
+
if (parts.length !== 3) {
|
|
10
|
+
throw new Error("Invalid token: JWT must have 3 parts");
|
|
11
|
+
}
|
|
12
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
13
|
+
// 1. Decode and parse Header
|
|
14
|
+
const headerStr = atob(headerB64.replace(/-/g, "+").replace(/_/g, "/"));
|
|
15
|
+
const header = JSON.parse(headerStr);
|
|
16
|
+
if (header.alg !== "RS256" || !header.kid) {
|
|
17
|
+
throw new Error("Unsupported algorithm or missing key ID (kid)");
|
|
18
|
+
}
|
|
19
|
+
// 2. Decode Payload and validate claims
|
|
20
|
+
const payload = decodeCredentials(credential);
|
|
21
|
+
const now = Math.floor(Date.now() / 1000);
|
|
22
|
+
if (payload.exp < now) {
|
|
23
|
+
throw new Error("Token has expired");
|
|
24
|
+
}
|
|
25
|
+
if (payload.iss !== "https://accounts.google.com" && payload.iss !== "accounts.google.com") {
|
|
26
|
+
throw new Error("Invalid issuer");
|
|
27
|
+
}
|
|
28
|
+
if (payload.aud !== clientId) {
|
|
29
|
+
throw new Error("Invalid audience (Client ID mismatch)");
|
|
30
|
+
}
|
|
31
|
+
// 3. Fetch Google public JWKS keys
|
|
32
|
+
const res = await fetch("https://www.googleapis.com/oauth2/v3/certs");
|
|
33
|
+
if (!res.ok) {
|
|
34
|
+
throw new Error("Failed to fetch Google public keys");
|
|
35
|
+
}
|
|
36
|
+
const jwks = (await res.json());
|
|
37
|
+
const jwk = jwks.keys.find((key) => key.kid === header.kid);
|
|
38
|
+
if (!jwk) {
|
|
39
|
+
throw new Error("Matching public key not found in Google certs");
|
|
40
|
+
}
|
|
41
|
+
// 4. Import public key into Web Crypto format
|
|
42
|
+
const subtle = crypto.subtle;
|
|
43
|
+
const cryptoKey = await subtle.importKey("jwk", jwk, {
|
|
44
|
+
name: "RSASSA-PKCS1-v1_5",
|
|
45
|
+
hash: { name: "SHA-256" },
|
|
46
|
+
}, false, ["verify"]);
|
|
47
|
+
// 5. Convert Signature from base64url to Uint8Array
|
|
48
|
+
const sigBase64 = signatureB64.replace(/-/g, "+").replace(/_/g, "/");
|
|
49
|
+
const pad = sigBase64.length % 4;
|
|
50
|
+
const paddedSig = pad ? sigBase64 + "=".repeat(4 - pad) : sigBase64;
|
|
51
|
+
const sigStr = atob(paddedSig);
|
|
52
|
+
const sigBytes = new Uint8Array(sigStr.length);
|
|
53
|
+
for (let i = 0; i < sigStr.length; i++) {
|
|
54
|
+
sigBytes[i] = sigStr.charCodeAt(i);
|
|
55
|
+
}
|
|
56
|
+
// 6. Verify signature against raw message bytes (header + "." + payload)
|
|
57
|
+
const enc = new TextEncoder();
|
|
58
|
+
const data = enc.encode(`${headerB64}.${payloadB64}`);
|
|
59
|
+
const isValid = await subtle.verify("RSASSA-PKCS1-v1_5", cryptoKey, sigBytes, data);
|
|
60
|
+
if (!isValid) {
|
|
61
|
+
throw new Error("Invalid signature");
|
|
62
|
+
}
|
|
63
|
+
return payload;
|
|
64
|
+
}
|