@plyaz/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/.github/pull_request_template.md +71 -0
- package/.github/workflows/deploy.yml +9 -0
- package/.github/workflows/publish.yml +14 -0
- package/.github/workflows/security.yml +20 -0
- package/README.md +89 -0
- package/commits.txt +5 -0
- package/dist/common/index.cjs +48 -0
- package/dist/common/index.cjs.map +1 -0
- package/dist/common/index.mjs +43 -0
- package/dist/common/index.mjs.map +1 -0
- package/dist/index.cjs +20411 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +5139 -0
- package/dist/index.mjs.map +1 -0
- package/eslint.config.mjs +13 -0
- package/index.html +13 -0
- package/package.json +141 -0
- package/src/adapters/auth-adapter-factory.ts +26 -0
- package/src/adapters/auth-adapter.mapper.ts +53 -0
- package/src/adapters/base-auth.adapter.ts +119 -0
- package/src/adapters/clerk/clerk.adapter.ts +204 -0
- package/src/adapters/custom/custom.adapter.ts +119 -0
- package/src/adapters/index.ts +4 -0
- package/src/adapters/next-auth/authOptions.ts +81 -0
- package/src/adapters/next-auth/next-auth.adapter.ts +211 -0
- package/src/api/client.ts +37 -0
- package/src/audit/audit.logger.ts +52 -0
- package/src/client/components/ProtectedRoute.tsx +37 -0
- package/src/client/hooks/useAuth.ts +128 -0
- package/src/client/hooks/useConnectedAccounts.ts +108 -0
- package/src/client/hooks/usePermissions.ts +36 -0
- package/src/client/hooks/useRBAC.ts +36 -0
- package/src/client/hooks/useSession.ts +18 -0
- package/src/client/providers/AuthProvider.tsx +104 -0
- package/src/client/store/auth.store.ts +306 -0
- package/src/client/utils/storage.ts +70 -0
- package/src/common/constants/oauth-providers.ts +49 -0
- package/src/common/errors/auth.errors.ts +64 -0
- package/src/common/errors/specific-auth-errors.ts +201 -0
- package/src/common/index.ts +19 -0
- package/src/common/regex/index.ts +27 -0
- package/src/common/types/auth.types.ts +641 -0
- package/src/common/types/index.ts +297 -0
- package/src/common/utils/index.ts +84 -0
- package/src/core/blacklist/token.blacklist.ts +60 -0
- package/src/core/index.ts +2 -0
- package/src/core/jwt/jwt.manager.ts +131 -0
- package/src/core/session/session.manager.ts +56 -0
- package/src/db/repositories/connected-account.repository.ts +415 -0
- package/src/db/repositories/role.repository.ts +519 -0
- package/src/db/repositories/session.repository.ts +308 -0
- package/src/db/repositories/user.repository.ts +320 -0
- package/src/flows/index.ts +2 -0
- package/src/flows/sign-in.flow.ts +106 -0
- package/src/flows/sign-up.flow.ts +121 -0
- package/src/index.ts +54 -0
- package/src/libs/clerk.helper.ts +36 -0
- package/src/libs/supabase.helper.ts +255 -0
- package/src/libs/supabaseClient.ts +6 -0
- package/src/providers/base/auth-provider.interface.ts +42 -0
- package/src/providers/base/index.ts +1 -0
- package/src/providers/index.ts +2 -0
- package/src/providers/oauth/facebook.provider.ts +97 -0
- package/src/providers/oauth/github.provider.ts +148 -0
- package/src/providers/oauth/google.provider.ts +126 -0
- package/src/providers/oauth/index.ts +3 -0
- package/src/rbac/dynamic-roles.ts +552 -0
- package/src/rbac/index.ts +4 -0
- package/src/rbac/permission-checker.ts +464 -0
- package/src/rbac/role-hierarchy.ts +545 -0
- package/src/rbac/role.manager.ts +75 -0
- package/src/security/csrf/csrf.protection.ts +37 -0
- package/src/security/index.ts +3 -0
- package/src/security/rate-limiting/auth/auth.controller.ts +12 -0
- package/src/security/rate-limiting/auth/rate-limiting.interface.ts +67 -0
- package/src/security/rate-limiting/auth.module.ts +32 -0
- package/src/server/auth.module.ts +158 -0
- package/src/server/decorators/auth.decorator.ts +43 -0
- package/src/server/decorators/auth.decorators.ts +31 -0
- package/src/server/decorators/current-user.decorator.ts +49 -0
- package/src/server/decorators/permission.decorator.ts +49 -0
- package/src/server/guards/auth.guard.ts +56 -0
- package/src/server/guards/custom-throttler.guard.ts +46 -0
- package/src/server/guards/permissions.guard.ts +115 -0
- package/src/server/guards/roles.guard.ts +31 -0
- package/src/server/middleware/auth.middleware.ts +46 -0
- package/src/server/middleware/index.ts +2 -0
- package/src/server/middleware/middleware.ts +11 -0
- package/src/server/middleware/session.middleware.ts +255 -0
- package/src/server/services/account.service.ts +269 -0
- package/src/server/services/auth.service.ts +79 -0
- package/src/server/services/brute-force.service.ts +98 -0
- package/src/server/services/index.ts +15 -0
- package/src/server/services/rate-limiter.service.ts +60 -0
- package/src/server/services/session.service.ts +287 -0
- package/src/server/services/token.service.ts +262 -0
- package/src/session/cookie-store.ts +255 -0
- package/src/session/enhanced-session-manager.ts +406 -0
- package/src/session/index.ts +14 -0
- package/src/session/memory-store.ts +320 -0
- package/src/session/redis-store.ts +443 -0
- package/src/strategies/oauth.strategy.ts +128 -0
- package/src/strategies/traditional-auth.strategy.ts +116 -0
- package/src/tokens/index.ts +4 -0
- package/src/tokens/refresh-token-manager.ts +448 -0
- package/src/tokens/token-validator.ts +311 -0
- package/tsconfig.build.json +28 -0
- package/tsconfig.json +38 -0
- package/tsup.config.mjs +28 -0
- package/vitest.config.mjs +16 -0
- package/vitest.setup.d.ts +2 -0
- package/vitest.setup.d.ts.map +1 -0
- package/vitest.setup.ts +1 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Facebook OAuth provider implementation
|
|
3
|
+
* @module @plyaz/auth/providers/oauth/facebook
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AuthTokens, AuthUser } from "@plyaz/types";
|
|
7
|
+
import { BaseAuthProvider } from "../base";
|
|
8
|
+
import type { ExpireAuthToken } from "./google.provider";
|
|
9
|
+
import { NUMERIX } from "@plyaz/config";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export class FacebookProvider extends BaseAuthProvider {
|
|
13
|
+
readonly name = 'facebook';
|
|
14
|
+
readonly type = 'oauth' as const;
|
|
15
|
+
|
|
16
|
+
async authenticate(credentials:{code:string}): Promise<{ user: AuthUser; tokens: AuthTokens }> {
|
|
17
|
+
const tokenResponse = await this.exchangeCodeForTokens(credentials.code) as { access_token: string; refresh_token: string; expires_in: number };
|
|
18
|
+
const userProfile = await this.getUserProfile(tokenResponse.access_token);
|
|
19
|
+
|
|
20
|
+
const tokens: ExpireAuthToken = {
|
|
21
|
+
accessToken: tokenResponse.access_token,
|
|
22
|
+
refreshToken: tokenResponse.refresh_token,
|
|
23
|
+
expiresAt: new Date(Date.now() + tokenResponse.expires_in * NUMERIX.THOUSAND),
|
|
24
|
+
expiresIn: 0,
|
|
25
|
+
tokenType: "",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return { user: userProfile, tokens };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async refreshToken(refreshToken: string): Promise<ExpireAuthToken> {
|
|
32
|
+
const response = await globalThis.fetch('https://graph.facebook.com/v18.0/oauth/access_token', {
|
|
33
|
+
method: 'GET',
|
|
34
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const data = await response.json() as { access_token: string; expires_in: number };
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
accessToken: data.access_token,
|
|
41
|
+
refreshToken: refreshToken,
|
|
42
|
+
expiresAt: new Date(Date.now() + data.expires_in * NUMERIX.THOUSAND),
|
|
43
|
+
expiresIn: 0,
|
|
44
|
+
tokenType: "",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async revokeToken(token: string): Promise<void> {
|
|
49
|
+
await globalThis.fetch(`https://graph.facebook.com/v18.0/me/permissions?access_token=${token}`, {
|
|
50
|
+
method: 'DELETE'
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async getUserProfile(accessToken: string): Promise<AuthUser> {
|
|
55
|
+
const response = await globalThis.fetch(`https://graph.facebook.com/v18.0/me?fields=id,name,email,picture&access_token=${accessToken}`);
|
|
56
|
+
const profile = await response.json() as { id: string; email: string; name: string; picture?: { data?: { url: string } } };
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
id: profile.id,
|
|
60
|
+
email: profile.email,
|
|
61
|
+
firstName: profile.name?.split(' ')[0],
|
|
62
|
+
lastName: profile.name?.split(' ').slice(1).join(' '),
|
|
63
|
+
displayName: profile.name,
|
|
64
|
+
avatarUrl: profile.picture?.data?.url,
|
|
65
|
+
authProvider: 'FACEBOOK',
|
|
66
|
+
isActive: true,
|
|
67
|
+
isVerified: true,
|
|
68
|
+
createdAt: new Date(),
|
|
69
|
+
updatedAt: new Date()
|
|
70
|
+
} as AuthUser;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
getAuthUrl(): string {
|
|
74
|
+
if (!this.config) {
|
|
75
|
+
throw new Error('Provider not initialized');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const params = new URLSearchParams({
|
|
79
|
+
client_id: this.config.clientId,
|
|
80
|
+
redirect_uri: this.config.redirectUri,
|
|
81
|
+
response_type: 'code',
|
|
82
|
+
scope: this.config.scopes?.join(',') ?? 'email,public_profile',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return `https://www.facebook.com/v18.0/dialog/oauth?${params.toString()}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private async exchangeCodeForTokens(code: string) : Promise<unknown>{
|
|
89
|
+
const response = await globalThis.fetch('https://graph.facebook.com/v18.0/oauth/access_token', {
|
|
90
|
+
method: 'GET',
|
|
91
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
92
|
+
});
|
|
93
|
+
globalThis.console.log(code)
|
|
94
|
+
|
|
95
|
+
return await response.json();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview GitHub OAuth provider implementation
|
|
3
|
+
* @module @plyaz/auth/providers/oauth/github
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AuthTokens, AuthUser } from "@plyaz/types";
|
|
7
|
+
import { BaseAuthProvider } from "../base";
|
|
8
|
+
import type { ExpireAuthToken } from "./google.provider";
|
|
9
|
+
import { NUMERIX } from "@plyaz/config";
|
|
10
|
+
import { Buffer } from 'buffer';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export class GitHubProvider extends BaseAuthProvider {
|
|
14
|
+
readonly name = "github";
|
|
15
|
+
readonly type = "oauth" as const;
|
|
16
|
+
|
|
17
|
+
async authenticate(credentials: {
|
|
18
|
+
code: string;
|
|
19
|
+
}): Promise<{ user: AuthUser; tokens: AuthTokens }> {
|
|
20
|
+
const tokenResponse = (await this.exchangeCodeForTokens(
|
|
21
|
+
credentials.code
|
|
22
|
+
)) as { access_token: string; refresh_token?: string; expires_in?: number };
|
|
23
|
+
const userProfile = await this.getUserProfile(tokenResponse.access_token);
|
|
24
|
+
|
|
25
|
+
const tokens: ExpireAuthToken = {
|
|
26
|
+
accessToken: tokenResponse.access_token,
|
|
27
|
+
refreshToken: tokenResponse.refresh_token ?? "",
|
|
28
|
+
expiresAt: new Date(
|
|
29
|
+
Date.now() + (tokenResponse.expires_in ?? NUMERIX.THIRTY_SIX_HUNDERD) * NUMERIX.THOUSAND
|
|
30
|
+
),
|
|
31
|
+
expiresIn: 0,
|
|
32
|
+
tokenType: "",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return { user: userProfile, tokens };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async refreshToken(refreshToken: string): Promise<ExpireAuthToken> {
|
|
39
|
+
const response = await globalThis.fetch(
|
|
40
|
+
"https://github.com/login/oauth/access_token",
|
|
41
|
+
{
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: {
|
|
44
|
+
Accept: "application/json",
|
|
45
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
46
|
+
},
|
|
47
|
+
body: new URLSearchParams({
|
|
48
|
+
client_id: this.config!.clientId,
|
|
49
|
+
client_secret: this.config!.clientSecret,
|
|
50
|
+
refresh_token: refreshToken,
|
|
51
|
+
grant_type: "refresh_token",
|
|
52
|
+
}),
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const data = (await response.json()) as {
|
|
57
|
+
access_token: string;
|
|
58
|
+
refresh_token?: string;
|
|
59
|
+
expires_in?: number;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
accessToken: data.access_token,
|
|
64
|
+
refreshToken: data.refresh_token ?? refreshToken,
|
|
65
|
+
expiresAt: new Date(Date.now() + (data.expires_in ?? NUMERIX.THIRTY_SIX_HUNDERD) * NUMERIX.THOUSAND),
|
|
66
|
+
expiresIn: 0,
|
|
67
|
+
tokenType: "",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async revokeToken(token: string): Promise<void> {
|
|
72
|
+
await globalThis.fetch(
|
|
73
|
+
`https://api.github.com/applications/${this.config!.clientId}/token`,
|
|
74
|
+
{
|
|
75
|
+
method: "DELETE",
|
|
76
|
+
headers: {
|
|
77
|
+
Authorization: `Basic ${Buffer.from(
|
|
78
|
+
`${this.config!.clientId}:${this.config!.clientSecret}`
|
|
79
|
+
).toString("base64")}`,
|
|
80
|
+
},
|
|
81
|
+
body: JSON.stringify({ access_token: token }),
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async getUserProfile(accessToken: string): Promise<AuthUser> {
|
|
87
|
+
const response = await globalThis.fetch("https://api.github.com/user", {
|
|
88
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const profile = (await response.json()) as {
|
|
92
|
+
id: number;
|
|
93
|
+
email: string;
|
|
94
|
+
name?: string;
|
|
95
|
+
login: string;
|
|
96
|
+
avatar_url: string;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
id: profile.id.toString(),
|
|
101
|
+
email: profile.email,
|
|
102
|
+
firstName: profile.name?.split(" ")[0],
|
|
103
|
+
lastName: profile.name?.split(" ").slice(1).join(" "),
|
|
104
|
+
displayName: profile.name ?? profile.login,
|
|
105
|
+
avatarUrl: profile.avatar_url,
|
|
106
|
+
authProvider: "GITHUB",
|
|
107
|
+
isActive: true,
|
|
108
|
+
isVerified: true,
|
|
109
|
+
createdAt: new Date(),
|
|
110
|
+
updatedAt: new Date(),
|
|
111
|
+
} as AuthUser;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
getAuthUrl(): string {
|
|
115
|
+
if (!this.config) {
|
|
116
|
+
throw new Error("Provider not initialized");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const params = new URLSearchParams({
|
|
120
|
+
client_id: this.config.clientId,
|
|
121
|
+
redirect_uri: this.config.redirectUri,
|
|
122
|
+
scope: this.config.scopes?.join(" ") ?? "user:email",
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return `https://github.com/login/oauth/authorize?${params.toString()}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private async exchangeCodeForTokens(code: string):Promise<unknown> {
|
|
129
|
+
const response = await globalThis.fetch(
|
|
130
|
+
"https://github.com/login/oauth/access_token",
|
|
131
|
+
{
|
|
132
|
+
method: "POST",
|
|
133
|
+
headers: {
|
|
134
|
+
Accept: "application/json",
|
|
135
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
136
|
+
},
|
|
137
|
+
body: new URLSearchParams({
|
|
138
|
+
client_id: this.config!.clientId,
|
|
139
|
+
client_secret: this.config!.clientSecret,
|
|
140
|
+
code,
|
|
141
|
+
redirect_uri: this.config!.redirectUri,
|
|
142
|
+
}),
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
return await response.json();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Google OAuth provider implementation
|
|
3
|
+
* @module @plyaz/auth/providers/oauth/google
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AuthUser, AuthTokens } from "@plyaz/types";
|
|
7
|
+
import { BaseAuthProvider } from "../base";
|
|
8
|
+
import { NUMERIX } from "@plyaz/config";
|
|
9
|
+
// Ensure AuthTokens includes expiresAt: Date in its definition
|
|
10
|
+
|
|
11
|
+
export interface ExpireAuthToken extends AuthTokens {
|
|
12
|
+
expiresAt: Date;
|
|
13
|
+
}
|
|
14
|
+
export class GoogleProvider extends BaseAuthProvider {
|
|
15
|
+
readonly name = "google";
|
|
16
|
+
readonly type = "oauth" as const;
|
|
17
|
+
|
|
18
|
+
async authenticate(credentials: {
|
|
19
|
+
code: string;
|
|
20
|
+
}): Promise<{ user: AuthUser; tokens: AuthTokens }> {
|
|
21
|
+
const tokenResponse = (await this.exchangeCodeForTokens(
|
|
22
|
+
credentials.code
|
|
23
|
+
)) as {
|
|
24
|
+
access_token: string;
|
|
25
|
+
refresh_token: string;
|
|
26
|
+
expires_in: number;
|
|
27
|
+
};
|
|
28
|
+
const userProfile = await this.getUserProfile(tokenResponse.access_token);
|
|
29
|
+
|
|
30
|
+
const tokens: ExpireAuthToken = {
|
|
31
|
+
accessToken: tokenResponse.access_token,
|
|
32
|
+
refreshToken: tokenResponse.refresh_token,
|
|
33
|
+
expiresAt: new Date(Date.now() + tokenResponse.expires_in * NUMERIX.THOUSAND),
|
|
34
|
+
expiresIn: 0,
|
|
35
|
+
tokenType: "",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return { user: userProfile, tokens };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async refreshToken(refreshToken: string): Promise<ExpireAuthToken> {
|
|
42
|
+
const response = await globalThis.fetch("https://oauth2.googleapis.com/token", {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
45
|
+
body: new URLSearchParams({
|
|
46
|
+
client_id: this.config!.clientId,
|
|
47
|
+
client_secret: this.config!.clientSecret,
|
|
48
|
+
refresh_token: refreshToken,
|
|
49
|
+
grant_type: "refresh_token",
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
accessToken: data.access_token,
|
|
57
|
+
refreshToken: data.refresh_token ?? refreshToken,
|
|
58
|
+
expiresAt: new Date(Date.now() + data.expires_in * NUMERIX.THOUSAND),
|
|
59
|
+
expiresIn: 0,
|
|
60
|
+
tokenType: "",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async revokeToken(token: string): Promise<void> {
|
|
65
|
+
await globalThis.fetch(`https://oauth2.googleapis.com/revoke?token=${token}`, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async getUserProfile(accessToken: string): Promise<AuthUser> {
|
|
71
|
+
const response = await globalThis.fetch(
|
|
72
|
+
"https://www.googleapis.com/oauth2/v2/userinfo",
|
|
73
|
+
{
|
|
74
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const profile = await response.json();
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
id: profile.id,
|
|
82
|
+
email: profile.email,
|
|
83
|
+
firstName: profile.given_name,
|
|
84
|
+
lastName: profile.family_name,
|
|
85
|
+
displayName:
|
|
86
|
+
profile.name ?? `${profile.given_name} ${profile.family_name}`,
|
|
87
|
+
avatarUrl: profile.picture,
|
|
88
|
+
authProvider: "GOOGLE",
|
|
89
|
+
isActive: true,
|
|
90
|
+
isVerified: profile.verified_email,
|
|
91
|
+
createdAt: new Date(),
|
|
92
|
+
updatedAt: new Date(),
|
|
93
|
+
} as AuthUser;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private async exchangeCodeForTokens(code: string):Promise<unknown> {
|
|
97
|
+
const response = await globalThis.fetch("https://oauth2.googleapis.com/token", {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
100
|
+
body: new URLSearchParams({
|
|
101
|
+
client_id: this.config!.clientId,
|
|
102
|
+
client_secret: this.config!.clientSecret,
|
|
103
|
+
code,
|
|
104
|
+
grant_type: "authorization_code",
|
|
105
|
+
redirect_uri: this.config!.redirectUri,
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return await response.json();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
getAuthUrl(): string {
|
|
113
|
+
if (!this.config) {
|
|
114
|
+
throw new Error("Provider not initialized");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const params = new URLSearchParams({
|
|
118
|
+
client_id: this.config.clientId,
|
|
119
|
+
redirect_uri: this.config.redirectUri,
|
|
120
|
+
response_type: "code",
|
|
121
|
+
scope: this.config.scopes?.join(" ") ?? "openid email profile",
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return `https://accounts.google.com/oauth/authorize?${params.toString()}`;
|
|
125
|
+
}
|
|
126
|
+
}
|