react-oauth-providers 1.0.0 → 1.0.1

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/src/actions.ts DELETED
@@ -1,156 +0,0 @@
1
- import { getSessionItem } from "./utils/session";
2
- import {
3
- getGoogleSessionUser,
4
- signinWithGoogleCallback,
5
- refreshGoogleAccessToken,
6
- } from "./google";
7
- import { getConfig } from "./authConfig";
8
- import { AUTH_PROVIDERS } from "./utils/constants";
9
- import { getFromStorage, removeFromStorage } from "./utils/storage";
10
-
11
- export { signinWithGoogleSpa as signinWithGoogleSpa } from "./google";
12
-
13
- let refreshTimeout: ReturnType<typeof setTimeout> | null = null;
14
-
15
- export const handleCallback = async () => {
16
- const config = getConfig();
17
- if (!config) return;
18
-
19
- const keys = config?.keys;
20
-
21
- const authMode = getSessionItem(keys.PROVIDER);
22
- const verifier = getSessionItem(keys.VERIFIER);
23
- if (!authMode || !verifier) return;
24
-
25
- try {
26
- switch (authMode) {
27
- case AUTH_PROVIDERS.google: {
28
- const searchQuery = new URLSearchParams(window.location.search);
29
- const code = searchQuery.get("code");
30
- if (!code) return;
31
-
32
- await signinWithGoogleCallback(code, verifier);
33
- break;
34
- }
35
-
36
- default:
37
- break;
38
- }
39
- } catch (error) {
40
- return Promise.reject(error);
41
- }
42
- };
43
-
44
- export const handleFetchSessionUser = async (retry = true) => {
45
- const config = getConfig();
46
- if (!config) return;
47
-
48
- const keys = config?.keys;
49
-
50
- const authMode = getFromStorage(config.storage!, keys.PROVIDER);
51
- if (!authMode) return;
52
-
53
- let sessionUser = null;
54
-
55
- try {
56
- switch (authMode) {
57
- case AUTH_PROVIDERS.google: {
58
- sessionUser = await getGoogleSessionUser();
59
- break;
60
- }
61
-
62
- default:
63
- break;
64
- }
65
-
66
- if (sessionUser) handleScheduleTokenRefresh();
67
-
68
- return sessionUser;
69
- } catch (error) {
70
- console.log("Fetch Session User Error:", error);
71
-
72
- if (!retry) return null;
73
-
74
- switch (authMode) {
75
- case AUTH_PROVIDERS.google: {
76
- await refreshGoogleAccessToken();
77
- await handleFetchSessionUser(false);
78
-
79
- break;
80
- }
81
-
82
- default:
83
- break;
84
- }
85
-
86
- return null;
87
- }
88
- };
89
-
90
- const handleScheduleTokenRefresh = () => {
91
- const config = getConfig();
92
- if (!config) return;
93
-
94
- const keys = config?.keys;
95
-
96
- const authMode = getFromStorage(config.storage!, keys.PROVIDER);
97
- if (!authMode) return;
98
-
99
- const expiresIn = Number(
100
- getFromStorage(config.storage!, keys.EXPIRES_IN) ?? 0
101
- );
102
- if (!expiresIn) return;
103
-
104
- clearTokenRefresh();
105
-
106
- // Refresh 60 seconds before expiry
107
- const refreshTime = expiresIn - 60 * 1000;
108
- refreshTimeout = setTimeout(async () => {
109
- switch (authMode) {
110
- case AUTH_PROVIDERS.google: {
111
- await refreshGoogleAccessToken();
112
- break;
113
- }
114
-
115
- default:
116
- break;
117
- }
118
- }, refreshTime);
119
- };
120
-
121
- const clearTokenRefresh = () => {
122
- if (refreshTimeout) {
123
- clearTimeout(refreshTimeout);
124
- refreshTimeout = null;
125
- }
126
- };
127
-
128
- export const logout = () => {
129
- clearTokens();
130
- clearTokenRefresh();
131
- };
132
-
133
- const clearTokens = () => {
134
- const config = getConfig();
135
- if (!config) return;
136
-
137
- const keys = config?.keys;
138
-
139
- const {
140
- PROVIDER,
141
- ACCESS_TOKEN,
142
- EXPIRES_IN,
143
- REFRESH_TOKEN,
144
- TOKEN_TYPE,
145
- ID_TOKEN,
146
- PROVIDER_TYPE,
147
- } = keys;
148
-
149
- removeFromStorage(config.storage!, PROVIDER);
150
- removeFromStorage(config.storage!, PROVIDER_TYPE);
151
- removeFromStorage(config.storage!, ACCESS_TOKEN);
152
- removeFromStorage(config.storage!, EXPIRES_IN);
153
- removeFromStorage(config.storage!, REFRESH_TOKEN);
154
- removeFromStorage(config.storage!, TOKEN_TYPE);
155
- removeFromStorage(config.storage!, ID_TOKEN);
156
- };
package/src/authConfig.ts DELETED
@@ -1,56 +0,0 @@
1
- import type { IAuthConfig, IAuthInternalConfig, Keys } from "./types";
2
- import { toURL, makeKey } from "./utils/others";
3
-
4
- export let config: IAuthInternalConfig | null = null;
5
-
6
- export const getConfig = () => config;
7
-
8
- export function createInternalConfig(
9
- passedConfig: IAuthConfig
10
- ): IAuthInternalConfig {
11
- // Set default values for internal config object
12
- const {
13
- preLogin = () => null,
14
- postLogin = () => null,
15
- onLogInError = () => null,
16
- storage = "local",
17
- spaCallbackUri = passedConfig.spaCallbackUri
18
- ? toURL(passedConfig.spaCallbackUri).toString()
19
- : window.location.origin,
20
- logoutRedirect = passedConfig.logoutRedirect
21
- ? toURL(passedConfig.logoutRedirect).toString()
22
- : window.location.origin,
23
- storageKeyPrefix = "auth_",
24
- googleProvider,
25
- }: IAuthConfig = passedConfig;
26
-
27
- config = {
28
- ...passedConfig,
29
- preLogin,
30
- postLogin,
31
- onLogInError,
32
- storage,
33
- storageKeyPrefix,
34
- spaCallbackUri,
35
- logoutRedirect,
36
- keys: createAuthKeys(storageKeyPrefix),
37
- googleProvider: googleProvider
38
- ? { type: "spa", scope: "openid email profile", ...googleProvider }
39
- : undefined,
40
- };
41
-
42
- return config;
43
- }
44
-
45
- export function createAuthKeys(prefix?: string): Record<keyof Keys, string> {
46
- return {
47
- PROVIDER_TYPE: makeKey("provider_type", prefix),
48
- PROVIDER: makeKey("provider", prefix),
49
- VERIFIER: makeKey("verifier", prefix),
50
- ACCESS_TOKEN: makeKey("access_token", prefix),
51
- EXPIRES_IN: makeKey("expires_in", prefix),
52
- REFRESH_TOKEN: makeKey("refresh_token", prefix),
53
- TOKEN_TYPE: makeKey("token_type", prefix),
54
- ID_TOKEN: makeKey("id_token", prefix),
55
- };
56
- }
@@ -1,157 +0,0 @@
1
- import http from "../utils/http";
2
- import { removeSessionItem, setSessionItem } from "../utils/session";
3
- import { generatePkce } from "../utils/pkce";
4
- import type { IAuthGoogleProviderType } from "../types";
5
- import { getConfig } from "../authConfig";
6
- import { AUTH_PROVIDERS } from "../utils/constants";
7
- import { getFromStorage, keepToStorage } from "../utils/storage";
8
-
9
- export const signinWithGoogleSpa = async () => {
10
- const config = getConfig();
11
- if (!config) return;
12
-
13
- const { googleProvider, keys } = config;
14
-
15
- const { verifier, challenge, algorithm } = await generatePkce();
16
-
17
- setSessionItem(keys.VERIFIER, verifier, 3 * 60 * 1000);
18
- setSessionItem(keys.PROVIDER, AUTH_PROVIDERS.google, 3 * 60 * 1000);
19
- setSessionItem(keys.PROVIDER_TYPE, googleProvider?.type!, 3 * 60 * 1000);
20
-
21
- const params = new URLSearchParams({
22
- client_id: googleProvider?.clientId!,
23
- redirect_uri: config?.spaCallbackUri!,
24
- response_type: "code",
25
- scope: googleProvider?.scope || "",
26
- code_challenge: challenge,
27
- code_challenge_method: algorithm,
28
- access_type: "offline",
29
- prompt: "consent",
30
- });
31
-
32
- window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
33
- };
34
-
35
- export const signinWithGoogleCallback = async (
36
- code: string,
37
- verifier: string
38
- ) => {
39
- if (!code || !verifier) return;
40
-
41
- const config = getConfig();
42
- if (!config) return;
43
-
44
- const { googleProvider, keys } = config;
45
- const type: IAuthGoogleProviderType = "spa";
46
-
47
- try {
48
- const data = new URLSearchParams({
49
- client_id: googleProvider?.clientId!,
50
- grant_type: "authorization_code",
51
- code: code,
52
- redirect_uri: config?.spaCallbackUri!,
53
- code_verifier: verifier,
54
- });
55
-
56
- const res = await http.post(
57
- "https://oauth2.googleapis.com/token",
58
- data.toString(),
59
- {
60
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
61
- }
62
- );
63
-
64
- const tokens = res.data as any;
65
-
66
- if (tokens?.access_token) {
67
- removeSessionItem(keys.PROVIDER);
68
- removeSessionItem(keys.VERIFIER);
69
- removeSessionItem(keys.PROVIDER_TYPE);
70
-
71
- keepToStorage(config.storage!, keys.PROVIDER, AUTH_PROVIDERS.google);
72
- keepToStorage(config.storage!, keys.PROVIDER_TYPE, type);
73
- keepToStorage(config.storage!, keys.ACCESS_TOKEN, tokens.access_token);
74
- keepToStorage(
75
- config.storage!,
76
- keys.EXPIRES_IN,
77
- String(Date.now() + tokens.expires_in * 1000)
78
- );
79
- keepToStorage(config.storage!, keys.REFRESH_TOKEN, tokens.refresh_token);
80
- keepToStorage(config.storage!, keys.TOKEN_TYPE, tokens.token_type);
81
- keepToStorage(config.storage!, keys.ID_TOKEN, tokens.id_token);
82
- }
83
- } catch (error) {
84
- return Promise.reject(error);
85
- }
86
- };
87
-
88
- export const getGoogleSessionUser = async () => {
89
- const config = getConfig();
90
- if (!config) return;
91
-
92
- const { keys } = config;
93
-
94
- try {
95
- const accessToken = getFromStorage(config.storage!, keys.ACCESS_TOKEN);
96
- if (!accessToken) return;
97
-
98
- const res = await http.get(
99
- `https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${accessToken}`
100
- );
101
-
102
- const user = res.data;
103
-
104
- return user;
105
- } catch (error) {
106
- return Promise.reject(error);
107
- }
108
- };
109
-
110
- export const refreshGoogleAccessToken = async () => {
111
- const config = getConfig();
112
- if (!config) return;
113
-
114
- const { googleProvider, keys } = config;
115
-
116
- const refreshToken = getFromStorage(config.storage!, keys.REFRESH_TOKEN);
117
-
118
- if (!refreshToken) return;
119
-
120
- try {
121
- const data = new URLSearchParams({
122
- client_id: googleProvider?.clientId!,
123
- grant_type: "refresh_token",
124
- refresh_token: refreshToken,
125
- });
126
-
127
- const res = await http.post(
128
- "https://oauth2.googleapis.com/token",
129
- data.toString(),
130
- {
131
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
132
- }
133
- );
134
-
135
- const tokens = res.data as any;
136
-
137
- if (!tokens.access_token) {
138
- console.error("Failed to refresh access token", tokens);
139
- return null;
140
- }
141
-
142
- keepToStorage(config.storage!, keys.ACCESS_TOKEN, tokens.access_token);
143
- if (tokens.expires_in) {
144
- keepToStorage(
145
- config.storage!,
146
- keys.EXPIRES_IN,
147
- String(Date.now() + tokens.expires_in * 1000)
148
- );
149
- }
150
- if (tokens.id_token)
151
- keepToStorage(config.storage!, keys.ID_TOKEN, tokens.id_token);
152
- if (tokens.token_type)
153
- keepToStorage(config.storage!, keys.TOKEN_TYPE, tokens.token_type);
154
- } catch (error) {
155
- return Promise.reject(error);
156
- }
157
- };
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- export { AuthContext } from './AuthContext'
2
- export { AuthProvider } from './AuthProvider'
3
- export { useAuth } from './useAuth'
4
-
5
- export type { IAuthContext, IAuthProvider, IAuthConfig } from './types'
package/src/types.ts DELETED
@@ -1,57 +0,0 @@
1
- import type { ReactNode } from "react";
2
-
3
- export interface IAuthContext {
4
- loginInProgress: boolean;
5
- signinWithGoogle: () => Promise<void>;
6
- logOut: ILogoutFunc;
7
- user?: any;
8
- error?: string | null;
9
- }
10
-
11
- export interface IAuthProvider {
12
- authConfig: IAuthConfig;
13
- children: ReactNode;
14
- }
15
-
16
- export type IAuthStorageType = "session" | "local";
17
-
18
- export type IAuthGoogleProviderType = "spa";
19
-
20
- export interface IAuthGoogleProvider {
21
- clientId: string;
22
- type?: IAuthGoogleProviderType;
23
- scope?: string;
24
- }
25
-
26
- // Input from users of the package, some optional values
27
- export type IAuthConfig = {
28
- googleProvider?: IAuthGoogleProvider;
29
- spaCallbackUri?: string;
30
- logoutRedirect?: string;
31
- preLogin?: () => void;
32
- postLogin?: () => void;
33
- onLogInError?: (err: any) => void;
34
- storageKeyPrefix?: string;
35
- storage?: IAuthStorageType;
36
-
37
- error?: string | null;
38
- };
39
-
40
- export type Keys = {
41
- PROVIDER_TYPE: string;
42
- PROVIDER: string;
43
- VERIFIER: string;
44
- ACCESS_TOKEN: string;
45
- EXPIRES_IN: string;
46
- REFRESH_TOKEN: string;
47
- TOKEN_TYPE: string;
48
- ID_TOKEN: string;
49
- };
50
-
51
- export type IAuthInternalConfig = IAuthConfig & { keys: Keys };
52
-
53
- export type ILogoutFunc = (args?: {
54
- logoutHint?: string;
55
- redirect?: boolean;
56
- redirectUri?: string;
57
- }) => void;
package/src/useAuth.tsx DELETED
@@ -1,13 +0,0 @@
1
- import { useContext } from 'react'
2
- import { AuthContext } from './AuthContext'
3
- import type { IAuthContext } from './types'
4
-
5
- export function useAuth(): IAuthContext {
6
- const ctx = useContext(AuthContext)
7
-
8
- if (!ctx) {
9
- throw new Error('useAuth must be used within an AuthProvider')
10
- }
11
-
12
- return ctx
13
- }
@@ -1 +0,0 @@
1
- export const AUTH_PROVIDERS = { google: 'google' } as const
package/src/utils/http.ts DELETED
@@ -1,51 +0,0 @@
1
- type RequestOptions = {
2
- headers?: Record<string, string>;
3
- body?: string;
4
- };
5
-
6
- async function request(
7
- url: string,
8
- options: RequestOptions & { method: string }
9
- ) {
10
- let body: any;
11
- const headers: Record<string, string> = { ...(options.headers ?? {}) };
12
-
13
- try {
14
- body = options.body;
15
- headers["Content-Type"] = headers["Content-Type"] || "application/json";
16
-
17
- const response = await fetch(url, {
18
- method: options.method,
19
- headers,
20
- body,
21
- });
22
-
23
- if (!response.ok) {
24
- const error = await response.json().catch(() => ({}));
25
- const expectedError = response.status >= 400 && response.status < 500;
26
-
27
- if (!expectedError) {
28
- console.log({ error });
29
- console.log("Unexpected error occurs");
30
- }
31
-
32
- return Promise.reject({ status: response.status, ...error });
33
- }
34
-
35
- return { data: await response.json() };
36
- } catch (error) {
37
- console.log("Network or unexpected error:", error);
38
- return Promise.reject(error);
39
- }
40
- }
41
-
42
- export default {
43
- get: (url: string, options?: RequestOptions) =>
44
- request(url, { ...options, method: "GET" }),
45
- post: (url: string, body?: string, options?: RequestOptions) =>
46
- request(url, { ...options, method: "POST", body }),
47
- put: (url: string, body?: string, options?: RequestOptions) =>
48
- request(url, { ...options, method: "PUT", body }),
49
- delete: (url: string, options?: RequestOptions) =>
50
- request(url, { ...options, method: "DELETE" }),
51
- };
@@ -1,13 +0,0 @@
1
- export const toURL = (input: string): URL => {
2
- // If input is absolute, URL works directly
3
- try {
4
- return new URL(input)
5
- } catch {
6
- // If input is relative, use window.location.origin as base
7
- return new URL(input, window.location.origin)
8
- }
9
- }
10
-
11
- export const makeKey = (key: string, prefix?: string) => {
12
- return prefix ? `${prefix}_${key}` : key
13
- }
package/src/utils/pkce.ts DELETED
@@ -1,15 +0,0 @@
1
- import getPkce from "oauth-pkce";
2
-
3
- export const generatePkce = async (): Promise<{
4
- verifier: string;
5
- challenge: string;
6
- algorithm: string;
7
- }> => {
8
- return new Promise((resolve, reject) => {
9
- getPkce(50, (error, { verifier, challenge }) => {
10
- if (error) reject(error);
11
-
12
- resolve({ verifier, challenge, algorithm: "S256" });
13
- });
14
- });
15
- };
@@ -1,33 +0,0 @@
1
- export function setSessionItem(key: string, value: string, ttlMs: number) {
2
- const data = {
3
- value,
4
- expires: Date.now() + ttlMs,
5
- }
6
-
7
- sessionStorage.setItem(key, JSON.stringify(data))
8
- }
9
-
10
- export function getSessionItem(key: string): string | null {
11
- const dataStr = sessionStorage.getItem(key)
12
- if (!dataStr) return null
13
-
14
- try {
15
- const data = JSON.parse(dataStr)
16
- if (Date.now() > data.expires) {
17
- sessionStorage.removeItem(key)
18
- return null
19
- }
20
-
21
- return data.value
22
- } catch {
23
- return null
24
- }
25
- }
26
-
27
- export function removeSessionItem(key: string) {
28
- try {
29
- sessionStorage.removeItem(key)
30
- } catch (error) {
31
- return null
32
- }
33
- }
@@ -1,18 +0,0 @@
1
- import type { IAuthStorageType } from '../types'
2
-
3
- export const keepToStorage = (type: IAuthStorageType, key: string, value: string) => {
4
- if (type === 'local') localStorage.setItem(key, value)
5
- else if (type === 'session') sessionStorage.setItem(key, value)
6
- }
7
-
8
- export const removeFromStorage = (type: IAuthStorageType, key: string) => {
9
- if (type === 'local') localStorage.removeItem(key)
10
- else if (type === 'session') sessionStorage.removeItem(key)
11
- }
12
-
13
- export const getFromStorage = (type: IAuthStorageType, key: string) => {
14
- if (type === 'local') return localStorage.getItem(key) ?? ''
15
- else if (type === 'session') return sessionStorage.getItem(key) ?? ''
16
-
17
- return ''
18
- }
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES6",
4
- "module": "ESNext",
5
- "declaration": true,
6
- "declarationDir": "dist/types",
7
- "outDir": "dist",
8
- "strict": true,
9
- "jsx": "react",
10
- "moduleResolution": "node",
11
- "esModuleInterop": true,
12
- "skipLibCheck": true
13
- },
14
- "include": ["src"]
15
- }