@startsimpli/auth 0.1.4 → 0.1.5

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.
@@ -1,191 +0,0 @@
1
- import { isTokenExpired, decodeToken } from '../chunk-AADTGAI2.mjs';
2
- import { hasRolePermission } from '../chunk-QAXZGJNQ.mjs';
3
- import { cookies } from 'next/headers';
4
- import { NextResponse } from 'next/server';
5
-
6
- async function getServerSession(apiBaseUrl) {
7
- const cookieStore = await cookies();
8
- const accessToken = cookieStore.get("access_token")?.value;
9
- if (!accessToken) {
10
- return null;
11
- }
12
- if (isTokenExpired(accessToken)) {
13
- return null;
14
- }
15
- try {
16
- const user = await fetchUser(apiBaseUrl, accessToken);
17
- const payload = decodeToken(accessToken);
18
- if (!payload) {
19
- return null;
20
- }
21
- return {
22
- user,
23
- accessToken,
24
- expiresAt: payload.exp * 1e3
25
- };
26
- } catch (error) {
27
- console.error("Failed to get server session:", error);
28
- return null;
29
- }
30
- }
31
- async function fetchUser(apiBaseUrl, accessToken) {
32
- const response = await fetch(`${apiBaseUrl}/api/v1/auth/me/`, {
33
- headers: {
34
- Authorization: `Bearer ${accessToken}`
35
- },
36
- cache: "no-store"
37
- });
38
- if (!response.ok) {
39
- throw new Error("Failed to fetch user");
40
- }
41
- return response.json();
42
- }
43
- async function validateSession(apiBaseUrl) {
44
- const session = await getServerSession(apiBaseUrl);
45
- return session?.user || null;
46
- }
47
- async function requireAuth(apiBaseUrl) {
48
- const session = await getServerSession(apiBaseUrl);
49
- if (!session) {
50
- throw new Error("Unauthorized");
51
- }
52
- return session;
53
- }
54
- async function refreshServerToken(apiBaseUrl) {
55
- try {
56
- const response = await fetch(`${apiBaseUrl}/api/v1/auth/token/refresh/`, {
57
- method: "POST",
58
- headers: {
59
- "Content-Type": "application/json"
60
- },
61
- credentials: "include"
62
- });
63
- if (!response.ok) {
64
- return null;
65
- }
66
- const data = await response.json();
67
- return data.access;
68
- } catch (error) {
69
- console.error("Token refresh failed:", error);
70
- return null;
71
- }
72
- }
73
- function createAuthMiddleware(config) {
74
- const {
75
- apiBaseUrl,
76
- publicPaths = ["/login", "/register", "/forgot-password"],
77
- loginPath = "/login"
78
- } = config;
79
- return async function authMiddleware(request) {
80
- const { pathname } = request.nextUrl;
81
- const isPublicPath = publicPaths.some(
82
- (path) => pathname.startsWith(path)
83
- );
84
- if (isPublicPath) {
85
- return NextResponse.next();
86
- }
87
- const accessToken = request.cookies.get("access_token")?.value;
88
- if (!accessToken || isTokenExpired(accessToken)) {
89
- const url = request.nextUrl.clone();
90
- url.pathname = loginPath;
91
- url.searchParams.set("from", pathname);
92
- return NextResponse.redirect(url);
93
- }
94
- return NextResponse.next();
95
- };
96
- }
97
- function hasValidToken(request) {
98
- const accessToken = request.cookies.get("access_token")?.value;
99
- if (!accessToken) {
100
- return false;
101
- }
102
- return !isTokenExpired(accessToken);
103
- }
104
- function getRequestToken(request) {
105
- const accessToken = request.cookies.get("access_token")?.value;
106
- if (!accessToken) {
107
- return null;
108
- }
109
- if (isTokenExpired(accessToken)) {
110
- return null;
111
- }
112
- return accessToken;
113
- }
114
- function getTokenFromRequest(req) {
115
- const authHeader = req.headers.get("authorization");
116
- if (authHeader?.startsWith("Bearer ")) {
117
- const token = authHeader.slice(7).trim();
118
- if (token) return token;
119
- }
120
- const cookieHeader = req.headers.get("cookie");
121
- if (cookieHeader) {
122
- const match = cookieHeader.split(";").map((part) => part.trim()).find((part) => part.startsWith("access_token="));
123
- if (match) {
124
- const token = match.slice("access_token=".length).trim();
125
- if (token) return token;
126
- }
127
- }
128
- return void 0;
129
- }
130
- async function requireAuthGuard(request) {
131
- const token = getRequestToken(request);
132
- if (!token) {
133
- return {
134
- authorized: false,
135
- response: NextResponse.json(
136
- { error: "Unauthorized" },
137
- { status: 401 }
138
- )
139
- };
140
- }
141
- return {
142
- authorized: true,
143
- token
144
- };
145
- }
146
- async function requireRoleGuard(request, requiredRole, getUserRole) {
147
- const authResult = await requireAuthGuard(request);
148
- if (!authResult.authorized) {
149
- return authResult;
150
- }
151
- const userRole = await getUserRole();
152
- if (!userRole || !hasRolePermission(userRole, requiredRole)) {
153
- return {
154
- authorized: false,
155
- response: NextResponse.json(
156
- { error: "Forbidden" },
157
- { status: 403 }
158
- )
159
- };
160
- }
161
- return {
162
- authorized: true,
163
- token: authResult.token
164
- };
165
- }
166
- function withAuth(handler) {
167
- return async (request) => {
168
- const guardResult = await requireAuthGuard(request);
169
- if (!guardResult.authorized) {
170
- return guardResult.response;
171
- }
172
- return handler(request, guardResult.token);
173
- };
174
- }
175
- function withRole(requiredRole, getUserRole, handler) {
176
- return async (request) => {
177
- const guardResult = await requireRoleGuard(
178
- request,
179
- requiredRole,
180
- getUserRole
181
- );
182
- if (!guardResult.authorized) {
183
- return guardResult.response;
184
- }
185
- return handler(request, guardResult.token);
186
- };
187
- }
188
-
189
- export { createAuthMiddleware, getRequestToken, getServerSession, getTokenFromRequest, hasValidToken, refreshServerToken, requireAuth, requireAuthGuard, requireRoleGuard, validateSession, withAuth, withRole };
190
- //# sourceMappingURL=index.mjs.map
191
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/server/session.ts","../../src/server/middleware.ts","../../src/server/guards.ts"],"names":["NextResponse"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Server-side session validation\n * For Next.js API routes and server components\n */\n\nimport { cookies } from 'next/headers';\nimport type { Session, AuthUser, TokenPayload } from '../types';\nimport { decodeToken, isTokenExpired } from '../utils';\n\n/**\n * Get session from server-side cookies and headers\n */\nexport async function getServerSession(\n apiBaseUrl: string\n): Promise<Session | null> {\n const cookieStore = await cookies();\n const accessToken = cookieStore.get('access_token')?.value;\n\n if (!accessToken) {\n return null;\n }\n\n if (isTokenExpired(accessToken)) {\n return null;\n }\n\n try {\n const user = await fetchUser(apiBaseUrl, accessToken);\n const payload = decodeToken(accessToken);\n\n if (!payload) {\n return null;\n }\n\n return {\n user,\n accessToken,\n expiresAt: payload.exp * 1000,\n };\n } catch (error) {\n console.error('Failed to get server session:', error);\n return null;\n }\n}\n\n/**\n * Fetch user data from Django backend\n */\nasync function fetchUser(\n apiBaseUrl: string,\n accessToken: string\n): Promise<AuthUser> {\n const response = await fetch(`${apiBaseUrl}/api/v1/auth/me/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n cache: 'no-store',\n });\n\n if (!response.ok) {\n throw new Error('Failed to fetch user');\n }\n\n return response.json();\n}\n\n/**\n * Validate session and return user or null\n */\nexport async function validateSession(\n apiBaseUrl: string\n): Promise<AuthUser | null> {\n const session = await getServerSession(apiBaseUrl);\n return session?.user || null;\n}\n\n/**\n * Require authenticated session (throws if not authenticated)\n */\nexport async function requireAuth(apiBaseUrl: string): Promise<Session> {\n const session = await getServerSession(apiBaseUrl);\n\n if (!session) {\n throw new Error('Unauthorized');\n }\n\n return session;\n}\n\n/**\n * Refresh access token using refresh token cookie\n */\nexport async function refreshServerToken(\n apiBaseUrl: string\n): Promise<string | null> {\n try {\n const response = await fetch(`${apiBaseUrl}/api/v1/auth/token/refresh/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n credentials: 'include',\n });\n\n if (!response.ok) {\n return null;\n }\n\n const data = await response.json();\n return data.access;\n } catch (error) {\n console.error('Token refresh failed:', error);\n return null;\n }\n}\n","/**\n * Next.js middleware helpers for authentication\n */\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport { isTokenExpired } from '../utils';\n\nexport interface AuthMiddlewareConfig {\n apiBaseUrl: string;\n publicPaths?: string[];\n loginPath?: string;\n}\n\n/**\n * Create auth middleware for Next.js\n */\nexport function createAuthMiddleware(config: AuthMiddlewareConfig) {\n const {\n apiBaseUrl,\n publicPaths = ['/login', '/register', '/forgot-password'],\n loginPath = '/login',\n } = config;\n\n return async function authMiddleware(request: NextRequest) {\n const { pathname } = request.nextUrl;\n\n const isPublicPath = publicPaths.some((path) =>\n pathname.startsWith(path)\n );\n\n if (isPublicPath) {\n return NextResponse.next();\n }\n\n const accessToken = request.cookies.get('access_token')?.value;\n\n if (!accessToken || isTokenExpired(accessToken)) {\n const url = request.nextUrl.clone();\n url.pathname = loginPath;\n url.searchParams.set('from', pathname);\n return NextResponse.redirect(url);\n }\n\n return NextResponse.next();\n };\n}\n\n/**\n * Check if request has valid auth token\n */\nexport function hasValidToken(request: NextRequest): boolean {\n const accessToken = request.cookies.get('access_token')?.value;\n\n if (!accessToken) {\n return false;\n }\n\n return !isTokenExpired(accessToken);\n}\n\n/**\n * Get access token from request\n */\nexport function getRequestToken(request: NextRequest): string | null {\n const accessToken = request.cookies.get('access_token')?.value;\n\n if (!accessToken) {\n return null;\n }\n\n if (isTokenExpired(accessToken)) {\n return null;\n }\n\n return accessToken;\n}\n\n/**\n * Extract bearer token from a standard Request (Authorization header or access_token cookie).\n * Framework-agnostic — works with any Request-compatible object (Next.js, Node, edge runtimes).\n * Returns the raw token string without expiry validation.\n */\nexport function getTokenFromRequest(req: Request): string | undefined {\n // Authorization: Bearer <token>\n const authHeader = req.headers.get('authorization');\n if (authHeader?.startsWith('Bearer ')) {\n const token = authHeader.slice(7).trim();\n if (token) return token;\n }\n\n // Fallback: access_token cookie (parsed from Cookie header)\n const cookieHeader = req.headers.get('cookie');\n if (cookieHeader) {\n const match = cookieHeader\n .split(';')\n .map((part) => part.trim())\n .find((part) => part.startsWith('access_token='));\n\n if (match) {\n const token = match.slice('access_token='.length).trim();\n if (token) return token;\n }\n }\n\n return undefined;\n}\n","/**\n * Auth guards for API routes\n */\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport type { CompanyRole } from '../types';\nimport { hasRolePermission } from '../types';\nimport { getRequestToken } from './middleware';\n\n/**\n * Auth guard result\n */\nexport interface GuardResult {\n authorized: boolean;\n response?: NextResponse;\n token?: string;\n}\n\n/**\n * Require authentication for API route\n */\nexport async function requireAuthGuard(\n request: NextRequest\n): Promise<GuardResult> {\n const token = getRequestToken(request);\n\n if (!token) {\n return {\n authorized: false,\n response: NextResponse.json(\n { error: 'Unauthorized' },\n { status: 401 }\n ),\n };\n }\n\n return {\n authorized: true,\n token,\n };\n}\n\n/**\n * Require specific role for API route\n */\nexport async function requireRoleGuard(\n request: NextRequest,\n requiredRole: CompanyRole,\n getUserRole: () => Promise<CompanyRole | null>\n): Promise<GuardResult> {\n const authResult = await requireAuthGuard(request);\n\n if (!authResult.authorized) {\n return authResult;\n }\n\n const userRole = await getUserRole();\n\n if (!userRole || !hasRolePermission(userRole, requiredRole)) {\n return {\n authorized: false,\n response: NextResponse.json(\n { error: 'Forbidden' },\n { status: 403 }\n ),\n };\n }\n\n return {\n authorized: true,\n token: authResult.token,\n };\n}\n\n/**\n * Higher-order function to wrap API route with auth guard\n */\nexport function withAuth<T = any>(\n handler: (request: NextRequest, token: string) => Promise<NextResponse<T>>\n) {\n return async (request: NextRequest): Promise<NextResponse> => {\n const guardResult = await requireAuthGuard(request);\n\n if (!guardResult.authorized) {\n return guardResult.response!;\n }\n\n return handler(request, guardResult.token!);\n };\n}\n\n/**\n * Higher-order function to wrap API route with role guard\n */\nexport function withRole<T = any>(\n requiredRole: CompanyRole,\n getUserRole: () => Promise<CompanyRole | null>,\n handler: (request: NextRequest, token: string) => Promise<NextResponse<T>>\n) {\n return async (request: NextRequest): Promise<NextResponse> => {\n const guardResult = await requireRoleGuard(\n request,\n requiredRole,\n getUserRole\n );\n\n if (!guardResult.authorized) {\n return guardResult.response!;\n }\n\n return handler(request, guardResult.token!);\n };\n}\n"]}
@@ -1,209 +0,0 @@
1
- /**
2
- * Authentication types for StartSimpli apps
3
- */
4
- /**
5
- * Generic token pair (access + optional refresh)
6
- */
7
- interface TokenPair {
8
- access: string;
9
- refresh?: string;
10
- }
11
- /**
12
- * Generic decoded JWT payload — framework and backend agnostic
13
- */
14
- interface DecodedToken {
15
- sub?: string;
16
- email?: string;
17
- exp?: number;
18
- iat?: number;
19
- [key: string]: unknown;
20
- }
21
- /**
22
- * Generic auth session — framework agnostic
23
- */
24
- interface AuthSession {
25
- user: {
26
- id: string;
27
- email: string;
28
- [key: string]: unknown;
29
- };
30
- accessToken: string;
31
- refreshToken?: string;
32
- }
33
- /**
34
- * User profile from Django backend
35
- */
36
- interface AuthUser {
37
- id: string;
38
- email: string;
39
- firstName: string;
40
- lastName: string;
41
- isEmailVerified: boolean;
42
- createdAt: string;
43
- updatedAt: string;
44
- companies?: Array<{
45
- id: string;
46
- name: string;
47
- role: 'owner' | 'admin' | 'member' | 'viewer';
48
- }>;
49
- currentCompanyId?: string;
50
- }
51
- /**
52
- * JWT token payload structure
53
- */
54
- interface TokenPayload {
55
- token_type: 'access';
56
- exp: number;
57
- iat: number;
58
- jti: string;
59
- user_id: string;
60
- }
61
- /**
62
- * Session data stored in client
63
- */
64
- interface Session {
65
- user: AuthUser;
66
- accessToken: string;
67
- expiresAt: number;
68
- }
69
- /**
70
- * Login response from Django backend
71
- */
72
- interface LoginResponse {
73
- access: string;
74
- user: AuthUser;
75
- }
76
- /**
77
- * Token refresh response
78
- */
79
- interface RefreshResponse {
80
- access: string;
81
- }
82
- /**
83
- * Permission check result
84
- */
85
- interface PermissionCheck {
86
- hasPermission: boolean;
87
- reason?: string;
88
- }
89
- /**
90
- * Auth configuration options
91
- */
92
- interface AuthConfig {
93
- apiBaseUrl: string;
94
- tokenRefreshInterval?: number;
95
- onSessionExpired?: () => void;
96
- onUnauthorized?: () => void;
97
- }
98
- /**
99
- * Auth state for React context
100
- */
101
- interface AuthState {
102
- session: Session | null;
103
- isLoading: boolean;
104
- isAuthenticated: boolean;
105
- }
106
- /**
107
- * Company role hierarchy
108
- */
109
- type CompanyRole = 'owner' | 'admin' | 'member' | 'viewer';
110
- /**
111
- * Role hierarchy map (higher number = more permissions)
112
- */
113
- declare const ROLE_HIERARCHY: Record<CompanyRole, number>;
114
- /**
115
- * Check if role has sufficient permissions
116
- */
117
- declare function hasRolePermission(userRole: CompanyRole, requiredRole: CompanyRole): boolean;
118
- /**
119
- * Password reset request payload
120
- * Initiates password reset flow by sending reset email
121
- */
122
- interface PasswordResetRequest {
123
- email: string;
124
- clientMetadata?: Record<string, any>;
125
- }
126
- /**
127
- * Password reset confirmation payload
128
- * Completes password reset with token and new password
129
- */
130
- interface PasswordResetConfirm {
131
- token: string;
132
- password: string;
133
- passwordConfirm: string;
134
- }
135
- /**
136
- * Email verification request payload
137
- * Verifies user email with token from verification email
138
- */
139
- interface EmailVerificationRequest {
140
- token: string;
141
- }
142
- /**
143
- * Email verification response
144
- * Returns updated user data after successful verification
145
- */
146
- interface EmailVerificationResponse {
147
- detail: string;
148
- user: {
149
- id: string;
150
- email: string;
151
- isEmailVerified: boolean;
152
- };
153
- }
154
- /**
155
- * API error response from Django backend
156
- * Standard error format for all API errors
157
- */
158
- interface ApiErrorResponse {
159
- detail?: string;
160
- message?: string;
161
- errors?: Record<string, string[]>;
162
- code?: string;
163
- status?: number;
164
- }
165
- /**
166
- * Validation error detail
167
- * Individual field validation error
168
- */
169
- interface ValidationError {
170
- field: string;
171
- message: string;
172
- code?: string;
173
- }
174
- /**
175
- * Validation errors map
176
- * Maps field names to error messages
177
- */
178
- type ValidationErrorsMap = Record<string, string[]>;
179
- /**
180
- * Password validation error codes
181
- */
182
- declare enum PasswordErrorCode {
183
- TOO_SHORT = "password_too_short",
184
- TOO_COMMON = "password_too_common",
185
- ENTIRELY_NUMERIC = "password_entirely_numeric",
186
- TOO_SIMILAR = "password_too_similar",
187
- MISMATCH = "password_mismatch",
188
- REQUIRED = "password_required"
189
- }
190
- /**
191
- * Email validation error codes
192
- */
193
- declare enum EmailErrorCode {
194
- INVALID_FORMAT = "email_invalid_format",
195
- REQUIRED = "email_required",
196
- NOT_FOUND = "email_not_found",
197
- ALREADY_EXISTS = "email_already_exists"
198
- }
199
- /**
200
- * General validation error codes
201
- */
202
- declare enum ValidationErrorCode {
203
- REQUIRED = "required",
204
- INVALID = "invalid",
205
- TOO_SHORT = "too_short",
206
- TOO_LONG = "too_long"
207
- }
208
-
209
- export { type ApiErrorResponse, type AuthConfig, type AuthSession, type AuthState, type AuthUser, type CompanyRole, type DecodedToken, EmailErrorCode, type EmailVerificationRequest, type EmailVerificationResponse, type LoginResponse, PasswordErrorCode, type PasswordResetConfirm, type PasswordResetRequest, type PermissionCheck, ROLE_HIERARCHY, type RefreshResponse, type Session, type TokenPair, type TokenPayload, type ValidationError, ValidationErrorCode, type ValidationErrorsMap, hasRolePermission };
@@ -1,209 +0,0 @@
1
- /**
2
- * Authentication types for StartSimpli apps
3
- */
4
- /**
5
- * Generic token pair (access + optional refresh)
6
- */
7
- interface TokenPair {
8
- access: string;
9
- refresh?: string;
10
- }
11
- /**
12
- * Generic decoded JWT payload — framework and backend agnostic
13
- */
14
- interface DecodedToken {
15
- sub?: string;
16
- email?: string;
17
- exp?: number;
18
- iat?: number;
19
- [key: string]: unknown;
20
- }
21
- /**
22
- * Generic auth session — framework agnostic
23
- */
24
- interface AuthSession {
25
- user: {
26
- id: string;
27
- email: string;
28
- [key: string]: unknown;
29
- };
30
- accessToken: string;
31
- refreshToken?: string;
32
- }
33
- /**
34
- * User profile from Django backend
35
- */
36
- interface AuthUser {
37
- id: string;
38
- email: string;
39
- firstName: string;
40
- lastName: string;
41
- isEmailVerified: boolean;
42
- createdAt: string;
43
- updatedAt: string;
44
- companies?: Array<{
45
- id: string;
46
- name: string;
47
- role: 'owner' | 'admin' | 'member' | 'viewer';
48
- }>;
49
- currentCompanyId?: string;
50
- }
51
- /**
52
- * JWT token payload structure
53
- */
54
- interface TokenPayload {
55
- token_type: 'access';
56
- exp: number;
57
- iat: number;
58
- jti: string;
59
- user_id: string;
60
- }
61
- /**
62
- * Session data stored in client
63
- */
64
- interface Session {
65
- user: AuthUser;
66
- accessToken: string;
67
- expiresAt: number;
68
- }
69
- /**
70
- * Login response from Django backend
71
- */
72
- interface LoginResponse {
73
- access: string;
74
- user: AuthUser;
75
- }
76
- /**
77
- * Token refresh response
78
- */
79
- interface RefreshResponse {
80
- access: string;
81
- }
82
- /**
83
- * Permission check result
84
- */
85
- interface PermissionCheck {
86
- hasPermission: boolean;
87
- reason?: string;
88
- }
89
- /**
90
- * Auth configuration options
91
- */
92
- interface AuthConfig {
93
- apiBaseUrl: string;
94
- tokenRefreshInterval?: number;
95
- onSessionExpired?: () => void;
96
- onUnauthorized?: () => void;
97
- }
98
- /**
99
- * Auth state for React context
100
- */
101
- interface AuthState {
102
- session: Session | null;
103
- isLoading: boolean;
104
- isAuthenticated: boolean;
105
- }
106
- /**
107
- * Company role hierarchy
108
- */
109
- type CompanyRole = 'owner' | 'admin' | 'member' | 'viewer';
110
- /**
111
- * Role hierarchy map (higher number = more permissions)
112
- */
113
- declare const ROLE_HIERARCHY: Record<CompanyRole, number>;
114
- /**
115
- * Check if role has sufficient permissions
116
- */
117
- declare function hasRolePermission(userRole: CompanyRole, requiredRole: CompanyRole): boolean;
118
- /**
119
- * Password reset request payload
120
- * Initiates password reset flow by sending reset email
121
- */
122
- interface PasswordResetRequest {
123
- email: string;
124
- clientMetadata?: Record<string, any>;
125
- }
126
- /**
127
- * Password reset confirmation payload
128
- * Completes password reset with token and new password
129
- */
130
- interface PasswordResetConfirm {
131
- token: string;
132
- password: string;
133
- passwordConfirm: string;
134
- }
135
- /**
136
- * Email verification request payload
137
- * Verifies user email with token from verification email
138
- */
139
- interface EmailVerificationRequest {
140
- token: string;
141
- }
142
- /**
143
- * Email verification response
144
- * Returns updated user data after successful verification
145
- */
146
- interface EmailVerificationResponse {
147
- detail: string;
148
- user: {
149
- id: string;
150
- email: string;
151
- isEmailVerified: boolean;
152
- };
153
- }
154
- /**
155
- * API error response from Django backend
156
- * Standard error format for all API errors
157
- */
158
- interface ApiErrorResponse {
159
- detail?: string;
160
- message?: string;
161
- errors?: Record<string, string[]>;
162
- code?: string;
163
- status?: number;
164
- }
165
- /**
166
- * Validation error detail
167
- * Individual field validation error
168
- */
169
- interface ValidationError {
170
- field: string;
171
- message: string;
172
- code?: string;
173
- }
174
- /**
175
- * Validation errors map
176
- * Maps field names to error messages
177
- */
178
- type ValidationErrorsMap = Record<string, string[]>;
179
- /**
180
- * Password validation error codes
181
- */
182
- declare enum PasswordErrorCode {
183
- TOO_SHORT = "password_too_short",
184
- TOO_COMMON = "password_too_common",
185
- ENTIRELY_NUMERIC = "password_entirely_numeric",
186
- TOO_SIMILAR = "password_too_similar",
187
- MISMATCH = "password_mismatch",
188
- REQUIRED = "password_required"
189
- }
190
- /**
191
- * Email validation error codes
192
- */
193
- declare enum EmailErrorCode {
194
- INVALID_FORMAT = "email_invalid_format",
195
- REQUIRED = "email_required",
196
- NOT_FOUND = "email_not_found",
197
- ALREADY_EXISTS = "email_already_exists"
198
- }
199
- /**
200
- * General validation error codes
201
- */
202
- declare enum ValidationErrorCode {
203
- REQUIRED = "required",
204
- INVALID = "invalid",
205
- TOO_SHORT = "too_short",
206
- TOO_LONG = "too_long"
207
- }
208
-
209
- export { type ApiErrorResponse, type AuthConfig, type AuthSession, type AuthState, type AuthUser, type CompanyRole, type DecodedToken, EmailErrorCode, type EmailVerificationRequest, type EmailVerificationResponse, type LoginResponse, PasswordErrorCode, type PasswordResetConfirm, type PasswordResetRequest, type PermissionCheck, ROLE_HIERARCHY, type RefreshResponse, type Session, type TokenPair, type TokenPayload, type ValidationError, ValidationErrorCode, type ValidationErrorsMap, hasRolePermission };
@@ -1,43 +0,0 @@
1
- 'use strict';
2
-
3
- // src/types/index.ts
4
- var ROLE_HIERARCHY = {
5
- owner: 4,
6
- admin: 3,
7
- member: 2,
8
- viewer: 1
9
- };
10
- function hasRolePermission(userRole, requiredRole) {
11
- return ROLE_HIERARCHY[userRole] >= ROLE_HIERARCHY[requiredRole];
12
- }
13
- var PasswordErrorCode = /* @__PURE__ */ ((PasswordErrorCode2) => {
14
- PasswordErrorCode2["TOO_SHORT"] = "password_too_short";
15
- PasswordErrorCode2["TOO_COMMON"] = "password_too_common";
16
- PasswordErrorCode2["ENTIRELY_NUMERIC"] = "password_entirely_numeric";
17
- PasswordErrorCode2["TOO_SIMILAR"] = "password_too_similar";
18
- PasswordErrorCode2["MISMATCH"] = "password_mismatch";
19
- PasswordErrorCode2["REQUIRED"] = "password_required";
20
- return PasswordErrorCode2;
21
- })(PasswordErrorCode || {});
22
- var EmailErrorCode = /* @__PURE__ */ ((EmailErrorCode2) => {
23
- EmailErrorCode2["INVALID_FORMAT"] = "email_invalid_format";
24
- EmailErrorCode2["REQUIRED"] = "email_required";
25
- EmailErrorCode2["NOT_FOUND"] = "email_not_found";
26
- EmailErrorCode2["ALREADY_EXISTS"] = "email_already_exists";
27
- return EmailErrorCode2;
28
- })(EmailErrorCode || {});
29
- var ValidationErrorCode = /* @__PURE__ */ ((ValidationErrorCode2) => {
30
- ValidationErrorCode2["REQUIRED"] = "required";
31
- ValidationErrorCode2["INVALID"] = "invalid";
32
- ValidationErrorCode2["TOO_SHORT"] = "too_short";
33
- ValidationErrorCode2["TOO_LONG"] = "too_long";
34
- return ValidationErrorCode2;
35
- })(ValidationErrorCode || {});
36
-
37
- exports.EmailErrorCode = EmailErrorCode;
38
- exports.PasswordErrorCode = PasswordErrorCode;
39
- exports.ROLE_HIERARCHY = ROLE_HIERARCHY;
40
- exports.ValidationErrorCode = ValidationErrorCode;
41
- exports.hasRolePermission = hasRolePermission;
42
- //# sourceMappingURL=index.js.map
43
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/types/index.ts"],"names":["PasswordErrorCode","EmailErrorCode","ValidationErrorCode"],"mappings":";;;AA8HO,IAAM,cAAA,GAA8C;AAAA,EACzD,KAAA,EAAO,CAAA;AAAA,EACP,KAAA,EAAO,CAAA;AAAA,EACP,MAAA,EAAQ,CAAA;AAAA,EACR,MAAA,EAAQ;AACV;AAKO,SAAS,iBAAA,CACd,UACA,YAAA,EACS;AACT,EAAA,OAAO,cAAA,CAAe,QAAQ,CAAA,IAAK,cAAA,CAAe,YAAY,CAAA;AAChE;AAyEO,IAAK,iBAAA,qBAAAA,kBAAAA,KAAL;AACL,EAAAA,mBAAA,WAAA,CAAA,GAAY,oBAAA;AACZ,EAAAA,mBAAA,YAAA,CAAA,GAAa,qBAAA;AACb,EAAAA,mBAAA,kBAAA,CAAA,GAAmB,2BAAA;AACnB,EAAAA,mBAAA,aAAA,CAAA,GAAc,sBAAA;AACd,EAAAA,mBAAA,UAAA,CAAA,GAAW,mBAAA;AACX,EAAAA,mBAAA,UAAA,CAAA,GAAW,mBAAA;AAND,EAAA,OAAAA,kBAAAA;AAAA,CAAA,EAAA,iBAAA,IAAA,EAAA;AAYL,IAAK,cAAA,qBAAAC,eAAAA,KAAL;AACL,EAAAA,gBAAA,gBAAA,CAAA,GAAiB,sBAAA;AACjB,EAAAA,gBAAA,UAAA,CAAA,GAAW,gBAAA;AACX,EAAAA,gBAAA,WAAA,CAAA,GAAY,iBAAA;AACZ,EAAAA,gBAAA,gBAAA,CAAA,GAAiB,sBAAA;AAJP,EAAA,OAAAA,eAAAA;AAAA,CAAA,EAAA,cAAA,IAAA,EAAA;AAUL,IAAK,mBAAA,qBAAAC,oBAAAA,KAAL;AACL,EAAAA,qBAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,qBAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,qBAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,qBAAA,UAAA,CAAA,GAAW,UAAA;AAJD,EAAA,OAAAA,oBAAAA;AAAA,CAAA,EAAA,mBAAA,IAAA,EAAA","file":"index.js","sourcesContent":["/**\n * Authentication types for StartSimpli apps\n */\n\n/**\n * Generic token pair (access + optional refresh)\n */\nexport interface TokenPair {\n access: string;\n refresh?: string;\n}\n\n/**\n * Generic decoded JWT payload — framework and backend agnostic\n */\nexport interface DecodedToken {\n sub?: string;\n email?: string;\n exp?: number;\n iat?: number;\n [key: string]: unknown;\n}\n\n/**\n * Generic auth session — framework agnostic\n */\nexport interface AuthSession {\n user: {\n id: string;\n email: string;\n [key: string]: unknown;\n };\n accessToken: string;\n refreshToken?: string;\n}\n\n/**\n * User profile from Django backend\n */\nexport interface AuthUser {\n id: string;\n email: string;\n firstName: string;\n lastName: string;\n isEmailVerified: boolean;\n createdAt: string;\n updatedAt: string;\n // Company/team context (if applicable)\n companies?: Array<{\n id: string;\n name: string;\n role: 'owner' | 'admin' | 'member' | 'viewer';\n }>;\n currentCompanyId?: string;\n}\n\n/**\n * JWT token payload structure\n */\nexport interface TokenPayload {\n token_type: 'access';\n exp: number;\n iat: number;\n jti: string;\n user_id: string;\n}\n\n/**\n * Session data stored in client\n */\nexport interface Session {\n user: AuthUser;\n accessToken: string;\n expiresAt: number;\n}\n\n/**\n * Login response from Django backend\n */\nexport interface LoginResponse {\n access: string;\n user: AuthUser;\n}\n\n/**\n * Token refresh response\n */\nexport interface RefreshResponse {\n access: string;\n}\n\n/**\n * Permission check result\n */\nexport interface PermissionCheck {\n hasPermission: boolean;\n reason?: string;\n}\n\n/**\n * Auth configuration options\n */\nexport interface AuthConfig {\n apiBaseUrl: string;\n tokenRefreshInterval?: number; // milliseconds, default 4 minutes\n onSessionExpired?: () => void;\n onUnauthorized?: () => void;\n}\n\n/**\n * Auth state for React context\n */\nexport interface AuthState {\n session: Session | null;\n isLoading: boolean;\n isAuthenticated: boolean;\n}\n\n/**\n * Company role hierarchy\n */\nexport type CompanyRole = 'owner' | 'admin' | 'member' | 'viewer';\n\n/**\n * Role hierarchy map (higher number = more permissions)\n */\nexport const ROLE_HIERARCHY: Record<CompanyRole, number> = {\n owner: 4,\n admin: 3,\n member: 2,\n viewer: 1,\n};\n\n/**\n * Check if role has sufficient permissions\n */\nexport function hasRolePermission(\n userRole: CompanyRole,\n requiredRole: CompanyRole\n): boolean {\n return ROLE_HIERARCHY[userRole] >= ROLE_HIERARCHY[requiredRole];\n}\n\n/**\n * Password reset request payload\n * Initiates password reset flow by sending reset email\n */\nexport interface PasswordResetRequest {\n email: string;\n clientMetadata?: Record<string, any>;\n}\n\n/**\n * Password reset confirmation payload\n * Completes password reset with token and new password\n */\nexport interface PasswordResetConfirm {\n token: string;\n password: string;\n passwordConfirm: string;\n}\n\n/**\n * Email verification request payload\n * Verifies user email with token from verification email\n */\nexport interface EmailVerificationRequest {\n token: string;\n}\n\n/**\n * Email verification response\n * Returns updated user data after successful verification\n */\nexport interface EmailVerificationResponse {\n detail: string;\n user: {\n id: string;\n email: string;\n isEmailVerified: boolean;\n };\n}\n\n/**\n * API error response from Django backend\n * Standard error format for all API errors\n */\nexport interface ApiErrorResponse {\n detail?: string;\n message?: string;\n errors?: Record<string, string[]>;\n code?: string;\n status?: number;\n}\n\n/**\n * Validation error detail\n * Individual field validation error\n */\nexport interface ValidationError {\n field: string;\n message: string;\n code?: string;\n}\n\n/**\n * Validation errors map\n * Maps field names to error messages\n */\nexport type ValidationErrorsMap = Record<string, string[]>;\n\n/**\n * Password validation error codes\n */\nexport enum PasswordErrorCode {\n TOO_SHORT = 'password_too_short',\n TOO_COMMON = 'password_too_common',\n ENTIRELY_NUMERIC = 'password_entirely_numeric',\n TOO_SIMILAR = 'password_too_similar',\n MISMATCH = 'password_mismatch',\n REQUIRED = 'password_required',\n}\n\n/**\n * Email validation error codes\n */\nexport enum EmailErrorCode {\n INVALID_FORMAT = 'email_invalid_format',\n REQUIRED = 'email_required',\n NOT_FOUND = 'email_not_found',\n ALREADY_EXISTS = 'email_already_exists',\n}\n\n/**\n * General validation error codes\n */\nexport enum ValidationErrorCode {\n REQUIRED = 'required',\n INVALID = 'invalid',\n TOO_SHORT = 'too_short',\n TOO_LONG = 'too_long',\n}\n"]}