@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.
Files changed (113) hide show
  1. package/.github/pull_request_template.md +71 -0
  2. package/.github/workflows/deploy.yml +9 -0
  3. package/.github/workflows/publish.yml +14 -0
  4. package/.github/workflows/security.yml +20 -0
  5. package/README.md +89 -0
  6. package/commits.txt +5 -0
  7. package/dist/common/index.cjs +48 -0
  8. package/dist/common/index.cjs.map +1 -0
  9. package/dist/common/index.mjs +43 -0
  10. package/dist/common/index.mjs.map +1 -0
  11. package/dist/index.cjs +20411 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.mjs +5139 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/eslint.config.mjs +13 -0
  16. package/index.html +13 -0
  17. package/package.json +141 -0
  18. package/src/adapters/auth-adapter-factory.ts +26 -0
  19. package/src/adapters/auth-adapter.mapper.ts +53 -0
  20. package/src/adapters/base-auth.adapter.ts +119 -0
  21. package/src/adapters/clerk/clerk.adapter.ts +204 -0
  22. package/src/adapters/custom/custom.adapter.ts +119 -0
  23. package/src/adapters/index.ts +4 -0
  24. package/src/adapters/next-auth/authOptions.ts +81 -0
  25. package/src/adapters/next-auth/next-auth.adapter.ts +211 -0
  26. package/src/api/client.ts +37 -0
  27. package/src/audit/audit.logger.ts +52 -0
  28. package/src/client/components/ProtectedRoute.tsx +37 -0
  29. package/src/client/hooks/useAuth.ts +128 -0
  30. package/src/client/hooks/useConnectedAccounts.ts +108 -0
  31. package/src/client/hooks/usePermissions.ts +36 -0
  32. package/src/client/hooks/useRBAC.ts +36 -0
  33. package/src/client/hooks/useSession.ts +18 -0
  34. package/src/client/providers/AuthProvider.tsx +104 -0
  35. package/src/client/store/auth.store.ts +306 -0
  36. package/src/client/utils/storage.ts +70 -0
  37. package/src/common/constants/oauth-providers.ts +49 -0
  38. package/src/common/errors/auth.errors.ts +64 -0
  39. package/src/common/errors/specific-auth-errors.ts +201 -0
  40. package/src/common/index.ts +19 -0
  41. package/src/common/regex/index.ts +27 -0
  42. package/src/common/types/auth.types.ts +641 -0
  43. package/src/common/types/index.ts +297 -0
  44. package/src/common/utils/index.ts +84 -0
  45. package/src/core/blacklist/token.blacklist.ts +60 -0
  46. package/src/core/index.ts +2 -0
  47. package/src/core/jwt/jwt.manager.ts +131 -0
  48. package/src/core/session/session.manager.ts +56 -0
  49. package/src/db/repositories/connected-account.repository.ts +415 -0
  50. package/src/db/repositories/role.repository.ts +519 -0
  51. package/src/db/repositories/session.repository.ts +308 -0
  52. package/src/db/repositories/user.repository.ts +320 -0
  53. package/src/flows/index.ts +2 -0
  54. package/src/flows/sign-in.flow.ts +106 -0
  55. package/src/flows/sign-up.flow.ts +121 -0
  56. package/src/index.ts +54 -0
  57. package/src/libs/clerk.helper.ts +36 -0
  58. package/src/libs/supabase.helper.ts +255 -0
  59. package/src/libs/supabaseClient.ts +6 -0
  60. package/src/providers/base/auth-provider.interface.ts +42 -0
  61. package/src/providers/base/index.ts +1 -0
  62. package/src/providers/index.ts +2 -0
  63. package/src/providers/oauth/facebook.provider.ts +97 -0
  64. package/src/providers/oauth/github.provider.ts +148 -0
  65. package/src/providers/oauth/google.provider.ts +126 -0
  66. package/src/providers/oauth/index.ts +3 -0
  67. package/src/rbac/dynamic-roles.ts +552 -0
  68. package/src/rbac/index.ts +4 -0
  69. package/src/rbac/permission-checker.ts +464 -0
  70. package/src/rbac/role-hierarchy.ts +545 -0
  71. package/src/rbac/role.manager.ts +75 -0
  72. package/src/security/csrf/csrf.protection.ts +37 -0
  73. package/src/security/index.ts +3 -0
  74. package/src/security/rate-limiting/auth/auth.controller.ts +12 -0
  75. package/src/security/rate-limiting/auth/rate-limiting.interface.ts +67 -0
  76. package/src/security/rate-limiting/auth.module.ts +32 -0
  77. package/src/server/auth.module.ts +158 -0
  78. package/src/server/decorators/auth.decorator.ts +43 -0
  79. package/src/server/decorators/auth.decorators.ts +31 -0
  80. package/src/server/decorators/current-user.decorator.ts +49 -0
  81. package/src/server/decorators/permission.decorator.ts +49 -0
  82. package/src/server/guards/auth.guard.ts +56 -0
  83. package/src/server/guards/custom-throttler.guard.ts +46 -0
  84. package/src/server/guards/permissions.guard.ts +115 -0
  85. package/src/server/guards/roles.guard.ts +31 -0
  86. package/src/server/middleware/auth.middleware.ts +46 -0
  87. package/src/server/middleware/index.ts +2 -0
  88. package/src/server/middleware/middleware.ts +11 -0
  89. package/src/server/middleware/session.middleware.ts +255 -0
  90. package/src/server/services/account.service.ts +269 -0
  91. package/src/server/services/auth.service.ts +79 -0
  92. package/src/server/services/brute-force.service.ts +98 -0
  93. package/src/server/services/index.ts +15 -0
  94. package/src/server/services/rate-limiter.service.ts +60 -0
  95. package/src/server/services/session.service.ts +287 -0
  96. package/src/server/services/token.service.ts +262 -0
  97. package/src/session/cookie-store.ts +255 -0
  98. package/src/session/enhanced-session-manager.ts +406 -0
  99. package/src/session/index.ts +14 -0
  100. package/src/session/memory-store.ts +320 -0
  101. package/src/session/redis-store.ts +443 -0
  102. package/src/strategies/oauth.strategy.ts +128 -0
  103. package/src/strategies/traditional-auth.strategy.ts +116 -0
  104. package/src/tokens/index.ts +4 -0
  105. package/src/tokens/refresh-token-manager.ts +448 -0
  106. package/src/tokens/token-validator.ts +311 -0
  107. package/tsconfig.build.json +28 -0
  108. package/tsconfig.json +38 -0
  109. package/tsup.config.mjs +28 -0
  110. package/vitest.config.mjs +16 -0
  111. package/vitest.setup.d.ts +2 -0
  112. package/vitest.setup.d.ts.map +1 -0
  113. package/vitest.setup.ts +1 -0
@@ -0,0 +1,106 @@
1
+ /**
2
+ * @fileoverview Sign-in authentication flow
3
+ * @module @plyaz/auth/flows/sign-in
4
+ */
5
+
6
+ import { NUMERIX } from "@plyaz/config";
7
+ import type { AuthTokens, AuthUser } from "@plyaz/types";
8
+
9
+ export interface SignInCredentials {
10
+ email: string;
11
+ password: string;
12
+ rememberMe?: boolean;
13
+ }
14
+
15
+ export interface SignInResult {
16
+ user: AuthUser;
17
+ tokens: AuthTokens;
18
+ requiresMFA?: boolean;
19
+ mfaToken?: string;
20
+ }
21
+
22
+ /** User repository type based on usage */
23
+ export interface UserRepositorySignin {
24
+ findByEmail(email: string): Promise<AuthUser | null>;
25
+ findById(userId: string): Promise<AuthUser | null>;
26
+ }
27
+
28
+ /** JWT manager type based on usage */
29
+ export interface JWTManagerSignin {
30
+ generateTokens(userId: string): Promise<AuthTokens>;
31
+ verifyToken(token: string): Promise<{ userId: string }>;
32
+ }
33
+
34
+ /** Session manager type based on usage */
35
+ export interface SessionManagerSignin {
36
+ createSession(session: { userId: string; expiresAt: Date; metadata?: Record<string, true> }): Promise<void>;
37
+ }
38
+
39
+ /** Password service type based on usage */
40
+ export interface PasswordServiceSignin {
41
+ verify(password: string, passwordHash: string | undefined): Promise<boolean>;
42
+ }
43
+
44
+ export class SignInFlow {
45
+ constructor(
46
+ private userRepository: UserRepositorySignin,
47
+ private jwtManager: JWTManagerSignin,
48
+ private sessionManager: SessionManagerSignin,
49
+ private passwordService: PasswordServiceSignin
50
+ ) {}
51
+
52
+ async execute(credentials: SignInCredentials): Promise<SignInResult> {
53
+ const isValid = await this.validateCredentials(credentials.email, credentials.password);
54
+ if (!isValid) {
55
+ throw new Error('Invalid credentials');
56
+ }
57
+
58
+ const user = await this.userRepository.findByEmail(credentials.email);
59
+ if (!user) {
60
+ throw new Error('User not found');
61
+ }
62
+
63
+ const tokens = await this.jwtManager.generateTokens(user.id);
64
+
65
+ if (credentials.rememberMe) {
66
+ const thirty = 30;
67
+ await this.sessionManager.createSession({
68
+ userId: user.id,
69
+ expiresAt: new Date(Date.now() + thirty * NUMERIX.TWENTY_FOUR * NUMERIX.SIXTY * NUMERIX.SIXTY * NUMERIX.THOUSAND), // 30 days
70
+ metadata: { rememberMe: true }
71
+ });
72
+ }
73
+
74
+ return { user, tokens };
75
+ }
76
+
77
+ async validateCredentials(email: string, password: string): Promise<boolean> {
78
+ const user = await this.userRepository.findByEmail(email);
79
+ if (!user) return false;
80
+
81
+ return await this.passwordService.verify(password, user.passwordHash);
82
+ }
83
+
84
+ async handleMFA(mfaToken: string, code: string): Promise<SignInResult> {
85
+ const payload = await this.jwtManager.verifyToken(mfaToken);
86
+ const user = await this.userRepository.findById(payload.userId);
87
+
88
+ if (!user) {
89
+ throw new Error('User not found');
90
+ }
91
+
92
+ const isValidCode = await this.verifyMFACode(user.id, code);
93
+ if (!isValidCode) {
94
+ throw new Error('Invalid MFA code');
95
+ }
96
+
97
+ const tokens = await this.jwtManager.generateTokens(user.id);
98
+ return { user, tokens };
99
+ }
100
+
101
+ private async verifyMFACode(userId: string, code: string): Promise<boolean> {
102
+ globalThis.console.log(userId,code)
103
+
104
+ return true; // Placeholder
105
+ }
106
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @fileoverview Sign-up authentication flow
3
+ * @module @plyaz/auth/flows/sign-up
4
+ */
5
+
6
+ import type { AuthTokens, AuthUser } from '@plyaz/types';
7
+
8
+ export interface SignUpData {
9
+ email: string;
10
+ password: string;
11
+ firstName?: string;
12
+ lastName?: string;
13
+ metadata?: Record<string, string>;
14
+ }
15
+
16
+ export interface SignUpResult {
17
+ user: AuthUser;
18
+ tokens: AuthTokens;
19
+ requiresVerification?: boolean;
20
+ }
21
+
22
+ /** User repository type based on usage */
23
+ export interface UserRepositorySignUp {
24
+ findByEmail(email: string): Promise<AuthUser | null>;
25
+ create(data: {
26
+ email: string;
27
+ passwordHash: string;
28
+ firstName?: string;
29
+ lastName?: string;
30
+ metadata?: Record<string, string >;
31
+ emailVerified: boolean;
32
+ }): Promise<AuthUser>;
33
+ findById(userId: string): Promise<AuthUser>;
34
+ updateEmailVerification(userId: string, verified: boolean): Promise<AuthUser | null>;
35
+ }
36
+
37
+ /** JWT manager type based on usage */
38
+ export interface JWTManagerSignUp {
39
+ generateTokens(userId: string): Promise<AuthTokens>;
40
+ generateVerificationToken(userId: string): Promise<string>;
41
+ verifyToken(token: string): Promise<{ userId: string }>;
42
+ }
43
+
44
+ /** Password service type based on usage */
45
+ export interface PasswordServiceSignUp {
46
+ hash(password: string): Promise<string>;
47
+ }
48
+
49
+ /** Email service type based on usage */
50
+ export interface EmailService {
51
+ sendVerificationEmail(args: { to: string; token: string; userId: string }): Promise<void>;
52
+ }
53
+
54
+ export class SignUpFlow {
55
+ constructor(
56
+ private userRepository: UserRepositorySignUp,
57
+ private jwtManager: JWTManagerSignUp,
58
+ private passwordService: PasswordServiceSignUp,
59
+ private emailService: EmailService
60
+ ) {}
61
+
62
+ async execute(data: SignUpData): Promise<SignUpResult> {
63
+ const emailExists = await this.userRepository.findByEmail(data.email);
64
+ if (emailExists) {
65
+ throw new Error('Email already exists');
66
+ }
67
+
68
+ const isValidEmail = await this.validateEmail(data.email);
69
+ if (!isValidEmail) {
70
+ throw new Error('Invalid email format');
71
+ }
72
+
73
+ const passwordHash = await this.passwordService.hash(data.password);
74
+
75
+ const user = await this.userRepository.create({
76
+ email: data.email,
77
+ passwordHash,
78
+ firstName: data.firstName,
79
+ lastName: data.lastName,
80
+ metadata: data.metadata,
81
+ emailVerified: false
82
+ });
83
+
84
+ const tokens = await this.jwtManager.generateTokens(user.id);
85
+
86
+ await this.sendVerificationEmail(user.id);
87
+
88
+ return {
89
+ user,
90
+ tokens,
91
+ requiresVerification: true
92
+ };
93
+ }
94
+
95
+ async validateEmail(email: string): Promise<boolean> {
96
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
97
+ return emailRegex.test(email);
98
+ }
99
+
100
+ async sendVerificationEmail(userId: string): Promise<void> {
101
+ const verificationToken = await this.jwtManager.generateVerificationToken(userId);
102
+ const user = await this.userRepository.findById(userId);
103
+
104
+ await this.emailService.sendVerificationEmail({
105
+ to: user.email,
106
+ token: verificationToken,
107
+ userId
108
+ });
109
+ }
110
+
111
+ async verifyEmail(token: string): Promise<AuthUser> {
112
+ const payload = await this.jwtManager.verifyToken(token);
113
+ const user = await this.userRepository.updateEmailVerification(payload.userId, true);
114
+
115
+ if (!user) {
116
+ throw new Error('User not found');
117
+ }
118
+
119
+ return user;
120
+ }
121
+ }
package/src/index.ts ADDED
@@ -0,0 +1,54 @@
1
+ // Common (Enums, Interfaces, Types, Constants, Errors, Regex)
2
+ export * from './common';
3
+
4
+
5
+
6
+ // Core Authentication
7
+ export * from './core';
8
+ export * from './rbac';
9
+ export * from './security';
10
+ export * from './audit/audit.logger';
11
+
12
+ // Session Management
13
+ export * from './session';
14
+
15
+
16
+
17
+ // Adapters
18
+ export * from './adapters';
19
+
20
+ // Strategies
21
+ export * from './strategies/traditional-auth.strategy';
22
+ export * from './strategies/oauth.strategy';
23
+
24
+
25
+
26
+ // Providers
27
+ export * from './providers';
28
+
29
+ // Flows
30
+ export * from './flows';
31
+
32
+ // Backend (NestJS)
33
+ export * from './server/services';
34
+ export * from './server/guards/auth.guard';
35
+ export * from './server/guards/roles.guard';
36
+ export * from './server/guards/permissions.guard';
37
+ export { Auth, Public } from './server/decorators/auth.decorator';
38
+ export { Roles, Permissions, CurrentSession } from './server/decorators/auth.decorators';
39
+ export { CurrentUser } from './server/decorators/current-user.decorator';
40
+ export { Permission as PermissionDecorator, Permissions as PermissionsDecorator, AnyPermission } from './server/decorators/permission.decorator';
41
+ export * from './server/middleware';
42
+
43
+
44
+
45
+ // Frontend (React)
46
+ export { AuthProvider } from './client/providers/AuthProvider';
47
+ export { useAuth } from './client/hooks/useAuth';
48
+ export { useSession } from './client/hooks/useSession';
49
+ export { useRBAC } from './client/hooks/useRBAC';
50
+ export { useConnectedAccounts } from './client/hooks/useConnectedAccounts';
51
+ export { usePermissions } from './client/hooks/usePermissions';
52
+ export { useAuthStore } from './client/store/auth.store';
53
+ export { ProtectedRoute } from './client/components/ProtectedRoute';
54
+
@@ -0,0 +1,36 @@
1
+ import "dotenv/config";
2
+ import { createClerkClient } from "@clerk/backend";
3
+ import { BaseError, DatabasePackageError } from "@plyaz/errors";
4
+
5
+ // Validate env early (VERY important)
6
+ if (!globalThis.process.env.CLERK_SECRET_KEY) {
7
+ throw new BaseError('CLIENT_MISSING_BASE_URL');
8
+ }
9
+
10
+ // Create Clerk client
11
+ const clerkClient = createClerkClient({
12
+ secretKey: globalThis.process.env.CLERK_SECRET_KEY,
13
+ });
14
+
15
+ globalThis.console.log("CLERK_SECRET_KEY loaded");
16
+
17
+ // Create OAuth Application
18
+ export const createClerkUser
19
+ = async ():Promise<void> =>{
20
+ try {
21
+ globalThis.console.log("Creating OAuth application...");
22
+
23
+ const response = await clerkClient.users.createUser({
24
+ firstName: "Test",
25
+ lastName: "User",
26
+ emailAddress: ["testclerk123@gmail.com"],
27
+ password: "!Testing233sw@qsdfefaf123!",
28
+ });
29
+ globalThis.console.dir("Full Response:", response);
30
+ } catch (error: unknown) {
31
+ if (error instanceof Error) {
32
+ throw new DatabasePackageError(error.message);
33
+ }
34
+ throw new DatabasePackageError('An unknown error occurred');
35
+ }
36
+ };
@@ -0,0 +1,255 @@
1
+ import type { AUTHPROVIDER, AuthUser, ConnectedAccount, Session } from '@plyaz/types';
2
+
3
+
4
+ import bcrypt from 'bcryptjs';
5
+ import { randomBytes } from 'crypto';
6
+ import { supabase } from './supabaseClient';
7
+ import { NUMERIX } from '@plyaz/config';
8
+
9
+ export interface DeviceInfo {
10
+ ip: string;
11
+ browser: string;
12
+ os: string;
13
+ userAgent: string;
14
+ }
15
+
16
+ /**
17
+ * Create a new user
18
+ */
19
+ export async function createUser(
20
+ email: string,
21
+ authProvider: string,
22
+ data?: Partial<AuthUser>,
23
+ password?: string
24
+ ): Promise<AuthUser> {
25
+ const passwordHash = password ? await bcrypt.hash(password, NUMERIX.TEN) : undefined;
26
+
27
+ const { data: user, error } = await supabase
28
+ .from('users')
29
+ .insert({
30
+ email,
31
+ auth_provider: authProvider,
32
+ first_name: data?.firstName,
33
+ last_name: data?.lastName,
34
+ display_name: data?.displayName ?? email,
35
+ avatar_url: data?.avatarUrl,
36
+ phone_number: data?.phoneNumber,
37
+ is_active: true,
38
+ is_verified: authProvider !== 'email', // email signups need verification
39
+ roles: data?.roles ?? [],
40
+ password_hash: passwordHash,
41
+ })
42
+ .select('*')
43
+ .single();
44
+
45
+ if (error || !user) throw new Error(error?.message ?? 'Failed to create user');
46
+ return user;
47
+ }
48
+
49
+ /**
50
+ * Find user by email + provider
51
+ */
52
+ export async function findUserByEmailProvider(
53
+ email: string,
54
+ provider: string
55
+ ): Promise<AuthUser | null> {
56
+ const { data, error } = await supabase
57
+ .from('users')
58
+ .select('*')
59
+ .eq('email', email)
60
+ .eq('auth_provider', provider)
61
+ .single();
62
+
63
+ if (error || !data) return null;
64
+ return data;
65
+ }
66
+
67
+ /**
68
+ * Update last login timestamp
69
+ */
70
+ export async function updateLastLogin(userId: string):Promise<void>{
71
+ await supabase
72
+ .from('users')
73
+ .update({ last_login_at: new Date() })
74
+ .eq('id', userId);
75
+ }
76
+
77
+ /**
78
+ * Create a new session
79
+ */
80
+ export async function createUserSession(
81
+ userId: string,
82
+ deviceInfo: DeviceInfo
83
+ ): Promise<Session & { accessToken: string }> {
84
+ const thirtyTwo = 32;
85
+ const accessToken = randomBytes(thirtyTwo).toString('hex');
86
+ const tokenHash = await bcrypt.hash(accessToken, NUMERIX.TEN);
87
+ const expiresAt = new Date(Date.now() + NUMERIX.THOUSAND * NUMERIX.SIXTY * NUMERIX.SIXTY * NUMERIX.TWENTY_FOUR); // 24h
88
+
89
+ const { data, error } = await supabase
90
+ .from('sessions')
91
+ .insert({
92
+ user_id: userId,
93
+ token_hash: tokenHash,
94
+ device_info: deviceInfo,
95
+ expires_at: expiresAt,
96
+ last_active_at: new Date(),
97
+ })
98
+ .select('*')
99
+ .single();
100
+
101
+ if (error || !data) throw new Error(error?.message ?? 'Failed to create session');
102
+
103
+ return {
104
+ id: data.id,
105
+ userId: data.user_id,
106
+ expiresAt: data.expires_at,
107
+ provider: data.provider,
108
+ createdAt: data.created_at,
109
+ lastActivityAt: data.last_active_at,
110
+ accessToken
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Get a session by ID
116
+ */
117
+ export async function getSession(sessionId: string): Promise<Session | null> {
118
+ const { data, error } = await supabase
119
+ .from('sessions')
120
+ .select('*')
121
+ .eq('id', sessionId)
122
+ .single();
123
+
124
+ if (error || !data) return null;
125
+
126
+ return {
127
+ id: data.id,
128
+ userId: data.user_id,
129
+ expiresAt: data.expires_at,
130
+ provider: data.provider,
131
+ createdAt: data.created_at,
132
+ lastActivityAt: data.last_active_at
133
+ };
134
+ }
135
+
136
+ /**
137
+ * Validate session (exists + not expired)
138
+ */
139
+ export async function validateSessionDB(sessionId: string): Promise<boolean> {
140
+ const { data } = await supabase
141
+ .from('sessions')
142
+ .select('id')
143
+ .eq('id', sessionId)
144
+ .gte('expires_at', new Date())
145
+ .single();
146
+
147
+ return !!data;
148
+ }
149
+
150
+ /**
151
+ * Delete session
152
+ */
153
+ export async function deleteSession(sessionId: string):Promise<void> {
154
+ await supabase.from('sessions').delete().eq('id', sessionId);
155
+ }
156
+
157
+ /**
158
+ * Refresh session
159
+ */
160
+ export async function refreshSessionDB(sessionId: string): Promise<Session & { accessToken: string }> {
161
+ const thirtyTwo = 32;
162
+ const accessToken = randomBytes(thirtyTwo).toString('hex');
163
+ const tokenHash = await bcrypt.hash(accessToken, NUMERIX.TEN);
164
+ const expiresAt = new Date(Date.now() + NUMERIX.THOUSAND * NUMERIX.SIXTY * NUMERIX.SIXTY * NUMERIX.TWENTY_FOUR); // 24h
165
+
166
+ const { data, error } = await supabase
167
+ .from('sessions')
168
+ .update({
169
+ token_hash: tokenHash,
170
+ expires_at: expiresAt,
171
+ last_active_at: new Date(),
172
+ })
173
+ .eq('id', sessionId)
174
+ .select('*')
175
+ .single();
176
+
177
+ if (error || !data) throw new Error(error?.message ?? 'Failed to refresh session');
178
+
179
+ return {
180
+ id: data.id,
181
+ userId: data.user_id,
182
+ expiresAt: data.expires_at,
183
+ provider: data.provider,
184
+ createdAt: data.created_at,
185
+ lastActivityAt: data.last_active_at,
186
+ accessToken
187
+ };
188
+ }
189
+
190
+
191
+ export async function linkConnectedAccount(
192
+ userId: string,
193
+ provider:AUTHPROVIDER,
194
+ data: ConnectedAccount
195
+ ) :Promise<ConnectedAccount>{
196
+ const { data: account, error } = await supabase
197
+ .from('connected_accounts')
198
+ .insert({
199
+ user_id: userId,
200
+ provider_type: data.providerType,
201
+ provider: provider,
202
+ provider_account_id: data.providerAccountId,
203
+ provider_email: data.providerEmail,
204
+ provider_username: data.providerUsername,
205
+ provider_display_name: data.providerDisplayName,
206
+ provider_avatar_url: data.providerAvatarUrl,
207
+ provider_profile_url: data.providerProfileUrl,
208
+ provider_metadata: data.providerMetadata ?? {},
209
+ wallet_address: data.walletAddress,
210
+ chain_id: data.chainId,
211
+ access_token_encrypted: data.accessTokenEncrypted,
212
+ refresh_token_encrypted: data.refreshTokenEncrypted,
213
+ token_expires_at: data.tokenExpiresAt,
214
+ token_scope: data.tokenScope,
215
+ is_primary: data.isPrimary ?? false,
216
+ is_verified: data.isVerified ?? true,
217
+ is_active: data.isActive ?? true,
218
+ linked_ip_address: data.linkedIpAddress,
219
+ linked_user_agent: data.linkedUserAgent,
220
+ })
221
+ .select('*')
222
+ .single();
223
+
224
+ if (error) throw new Error(error.message);
225
+ return account;
226
+ }
227
+
228
+ /**
229
+ * Unlink a provider account from a user
230
+ */
231
+ export async function unlinkConnectedAccount(
232
+ userId: string,
233
+ accountId: string
234
+ ):Promise<void>{
235
+ const { error } = await supabase
236
+ .from('connected_accounts')
237
+ .delete()
238
+ .eq('user_id', userId)
239
+ .eq('id', accountId);
240
+
241
+ if (error) throw new Error(error.message);
242
+ }
243
+
244
+ /**
245
+ * Get all linked accounts for a user
246
+ */
247
+ export async function getLinkedAccounts(userId: string):Promise<ConnectedAccount[] >{
248
+ const { data, error } = await supabase
249
+ .from('connected_accounts')
250
+ .select('*')
251
+ .eq('user_id', userId);
252
+
253
+ if (error) throw new Error(error.message);
254
+ return data || [];
255
+ }
@@ -0,0 +1,6 @@
1
+ import { createClient } from '@supabase/supabase-js';
2
+
3
+ export const supabase = createClient(
4
+ globalThis.process.env.NEXT_PUBLIC_SUPABASE_URL!,
5
+ globalThis.process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
6
+ );
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @fileoverview Base authentication provider interface
3
+ * @module @plyaz/auth/providers/base/auth-provider
4
+ */
5
+
6
+ import type { AuthTokens, AuthUser } from "@plyaz/types";
7
+
8
+
9
+
10
+ export interface AuthProviderConfig {
11
+ clientId: string;
12
+ clientSecret: string;
13
+ redirectUri: string;
14
+ scopes?: string[];
15
+ }
16
+
17
+ export interface AuthProvider {
18
+ readonly name: string;
19
+ readonly type: 'oauth' | 'traditional' | 'web3';
20
+
21
+ initialize(config: AuthProviderConfig): Promise<void>;
22
+ authenticate(credentials: {code:string}): Promise<{ user: AuthUser; tokens: AuthTokens }>;
23
+ refreshToken(refreshToken: string): Promise<AuthTokens>;
24
+ revokeToken(token: string): Promise<void>;
25
+ getUserProfile(accessToken: string): Promise<AuthUser>;
26
+ }
27
+
28
+ export abstract class BaseAuthProvider implements AuthProvider {
29
+ abstract readonly name: string;
30
+ abstract readonly type: 'oauth' | 'traditional' | 'web3';
31
+
32
+ protected config?: AuthProviderConfig;
33
+
34
+ async initialize(config: AuthProviderConfig): Promise<void> {
35
+ this.config = config;
36
+ }
37
+
38
+ abstract authenticate(credentials: {code:string}): Promise<{ user: AuthUser; tokens: AuthTokens }>;
39
+ abstract refreshToken(refreshToken: string): Promise<AuthTokens>;
40
+ abstract revokeToken(token: string): Promise<void>;
41
+ abstract getUserProfile(accessToken: string): Promise<AuthUser>;
42
+ }
@@ -0,0 +1 @@
1
+ export * from "./auth-provider.interface"
@@ -0,0 +1,2 @@
1
+ export * from "./base";
2
+ export * from "./oauth"