@caffeinebounce/identity 0.12.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/LICENSE +21 -0
- package/README.md +46 -0
- package/dist/index.d.mts +778 -0
- package/dist/index.d.ts +778 -0
- package/dist/index.js +5847 -0
- package/dist/index.mjs +5795 -0
- package/dist/server.d.mts +110 -0
- package/dist/server.d.ts +110 -0
- package/dist/server.js +295 -0
- package/dist/server.mjs +277 -0
- package/package.json +84 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { SupabaseClient, User, EmailOtpType } from '@supabase/supabase-js';
|
|
2
|
+
export { GeolocationInfo, generateSecureToken, getClientIP, getGeolocationFromIP, hashString } from '@caffeinebounce/shared-utils';
|
|
3
|
+
|
|
4
|
+
type AuthCallbackFlow = "oauth" | "otp";
|
|
5
|
+
type AuthCallbackHookErrorMode = "block" | "ignore";
|
|
6
|
+
type AuthCallbackRedirectTarget = string | URL | Response | null | undefined;
|
|
7
|
+
type AuthCallbackErrorSource = "oauth_error" | "code_exchange";
|
|
8
|
+
interface AuthCallbackHookContext {
|
|
9
|
+
/** Resolved Supabase client after callback exchange/verification succeeds */
|
|
10
|
+
supabase: SupabaseClient;
|
|
11
|
+
/** Authenticated Supabase user returned by getUser() */
|
|
12
|
+
user: User;
|
|
13
|
+
/** Original callback request */
|
|
14
|
+
request: Request;
|
|
15
|
+
/** Request origin used for final redirects */
|
|
16
|
+
origin: string;
|
|
17
|
+
/** Safe relative redirect path selected for this callback */
|
|
18
|
+
redirectPath: string;
|
|
19
|
+
/** Callback flow that established the session */
|
|
20
|
+
flow: AuthCallbackFlow;
|
|
21
|
+
/** OTP verification type, present for token_hash callbacks */
|
|
22
|
+
otpType?: EmailOtpType;
|
|
23
|
+
}
|
|
24
|
+
type AuthCallbackHook = (context: AuthCallbackHookContext) => void | Promise<void>;
|
|
25
|
+
type AuthCallbackSuccessRedirectResolver = (context: AuthCallbackHookContext) => AuthCallbackRedirectTarget | Promise<AuthCallbackRedirectTarget>;
|
|
26
|
+
interface AuthCallbackLinkingFlowContext {
|
|
27
|
+
/** Original callback request */
|
|
28
|
+
request: Request;
|
|
29
|
+
/** Request origin used for redirects */
|
|
30
|
+
origin: string;
|
|
31
|
+
/** Safe relative redirect path selected for this callback */
|
|
32
|
+
redirectPath: string;
|
|
33
|
+
}
|
|
34
|
+
type AuthCallbackLinkingFlowDetector = (context: AuthCallbackLinkingFlowContext) => boolean;
|
|
35
|
+
interface AuthCallbackLinkingErrorContext extends AuthCallbackLinkingFlowContext {
|
|
36
|
+
/** OAuth error code or code-exchange error name when available */
|
|
37
|
+
error: string;
|
|
38
|
+
/** OAuth error description, when supplied by the provider */
|
|
39
|
+
errorDescription: string | null;
|
|
40
|
+
/** Raw provider or exchange error message */
|
|
41
|
+
message: string;
|
|
42
|
+
/** Product-neutral fallback message selected by the handler */
|
|
43
|
+
defaultMessage: string;
|
|
44
|
+
/** Callback phase that produced the error */
|
|
45
|
+
source: AuthCallbackErrorSource;
|
|
46
|
+
}
|
|
47
|
+
type AuthCallbackLinkingErrorMessageResolver = (context: AuthCallbackLinkingErrorContext) => string | Promise<string>;
|
|
48
|
+
interface AuthCallbackConfig {
|
|
49
|
+
/** Async Supabase client factory (for server-side) */
|
|
50
|
+
createClient: () => Promise<SupabaseClient>;
|
|
51
|
+
/** Default redirect path after successful auth */
|
|
52
|
+
defaultRedirect?: string;
|
|
53
|
+
/** Sign-in page path for error redirects */
|
|
54
|
+
signInPath?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Optional hook that runs after a successful auth callback and getUser().
|
|
57
|
+
* Use this for app-local profile hydration or other session-adjacent setup.
|
|
58
|
+
*/
|
|
59
|
+
postAuthHook?: AuthCallbackHook;
|
|
60
|
+
/**
|
|
61
|
+
* How to handle postAuthHook failures.
|
|
62
|
+
* "block" redirects to signInPath with a generic setup error.
|
|
63
|
+
* "ignore" allows the normal success redirect to continue.
|
|
64
|
+
*/
|
|
65
|
+
postAuthHookErrorMode?: AuthCallbackHookErrorMode;
|
|
66
|
+
/**
|
|
67
|
+
* Optional resolver for app-specific success redirects after auth succeeds.
|
|
68
|
+
* Use this for product-owned routing such as admin approval or subdomains.
|
|
69
|
+
*/
|
|
70
|
+
resolveSuccessRedirect?: AuthCallbackSuccessRedirectResolver;
|
|
71
|
+
/**
|
|
72
|
+
* Optional detector for deciding whether an auth error came from account
|
|
73
|
+
* linking. Defaults to common profile/settings redirect paths.
|
|
74
|
+
*/
|
|
75
|
+
isLinkingFlow?: AuthCallbackLinkingFlowDetector;
|
|
76
|
+
/**
|
|
77
|
+
* Optional resolver for product-specific account-linking error copy.
|
|
78
|
+
* Defaults remain product-neutral.
|
|
79
|
+
*/
|
|
80
|
+
resolveLinkingErrorMessage?: AuthCallbackLinkingErrorMessageResolver;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* createAuthCallbackHandler - Factory for OAuth/email callback route handler
|
|
84
|
+
*
|
|
85
|
+
* Creates a GET handler that exchanges authorization codes or verifies
|
|
86
|
+
* token_hash OTP links for sessions.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* // app/(auth)/callback/route.ts
|
|
91
|
+
* import { createAuthCallbackHandler } from "@caffeinebounce/identity";
|
|
92
|
+
* import { createClient } from "@/lib/supabase/server";
|
|
93
|
+
*
|
|
94
|
+
* export const GET = createAuthCallbackHandler({
|
|
95
|
+
* createClient,
|
|
96
|
+
* defaultRedirect: "/dashboard",
|
|
97
|
+
* postAuthHook: async ({ user }) => {
|
|
98
|
+
* await ensureProfileRecord({
|
|
99
|
+
* userId: user.id,
|
|
100
|
+
* email: user.email ?? null,
|
|
101
|
+
* metadata: user.user_metadata,
|
|
102
|
+
* });
|
|
103
|
+
* },
|
|
104
|
+
* postAuthHookErrorMode: "ignore",
|
|
105
|
+
* });
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
declare function createAuthCallbackHandler({ createClient, defaultRedirect, signInPath, postAuthHook, postAuthHookErrorMode, resolveSuccessRedirect, isLinkingFlow, resolveLinkingErrorMessage, }: AuthCallbackConfig): (request: Request) => Promise<Response>;
|
|
109
|
+
|
|
110
|
+
export { type AuthCallbackConfig, type AuthCallbackErrorSource, type AuthCallbackFlow, type AuthCallbackHook, type AuthCallbackHookContext, type AuthCallbackHookErrorMode, type AuthCallbackLinkingErrorContext, type AuthCallbackLinkingErrorMessageResolver, type AuthCallbackLinkingFlowContext, type AuthCallbackLinkingFlowDetector, type AuthCallbackRedirectTarget, type AuthCallbackSuccessRedirectResolver, createAuthCallbackHandler };
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { SupabaseClient, User, EmailOtpType } from '@supabase/supabase-js';
|
|
2
|
+
export { GeolocationInfo, generateSecureToken, getClientIP, getGeolocationFromIP, hashString } from '@caffeinebounce/shared-utils';
|
|
3
|
+
|
|
4
|
+
type AuthCallbackFlow = "oauth" | "otp";
|
|
5
|
+
type AuthCallbackHookErrorMode = "block" | "ignore";
|
|
6
|
+
type AuthCallbackRedirectTarget = string | URL | Response | null | undefined;
|
|
7
|
+
type AuthCallbackErrorSource = "oauth_error" | "code_exchange";
|
|
8
|
+
interface AuthCallbackHookContext {
|
|
9
|
+
/** Resolved Supabase client after callback exchange/verification succeeds */
|
|
10
|
+
supabase: SupabaseClient;
|
|
11
|
+
/** Authenticated Supabase user returned by getUser() */
|
|
12
|
+
user: User;
|
|
13
|
+
/** Original callback request */
|
|
14
|
+
request: Request;
|
|
15
|
+
/** Request origin used for final redirects */
|
|
16
|
+
origin: string;
|
|
17
|
+
/** Safe relative redirect path selected for this callback */
|
|
18
|
+
redirectPath: string;
|
|
19
|
+
/** Callback flow that established the session */
|
|
20
|
+
flow: AuthCallbackFlow;
|
|
21
|
+
/** OTP verification type, present for token_hash callbacks */
|
|
22
|
+
otpType?: EmailOtpType;
|
|
23
|
+
}
|
|
24
|
+
type AuthCallbackHook = (context: AuthCallbackHookContext) => void | Promise<void>;
|
|
25
|
+
type AuthCallbackSuccessRedirectResolver = (context: AuthCallbackHookContext) => AuthCallbackRedirectTarget | Promise<AuthCallbackRedirectTarget>;
|
|
26
|
+
interface AuthCallbackLinkingFlowContext {
|
|
27
|
+
/** Original callback request */
|
|
28
|
+
request: Request;
|
|
29
|
+
/** Request origin used for redirects */
|
|
30
|
+
origin: string;
|
|
31
|
+
/** Safe relative redirect path selected for this callback */
|
|
32
|
+
redirectPath: string;
|
|
33
|
+
}
|
|
34
|
+
type AuthCallbackLinkingFlowDetector = (context: AuthCallbackLinkingFlowContext) => boolean;
|
|
35
|
+
interface AuthCallbackLinkingErrorContext extends AuthCallbackLinkingFlowContext {
|
|
36
|
+
/** OAuth error code or code-exchange error name when available */
|
|
37
|
+
error: string;
|
|
38
|
+
/** OAuth error description, when supplied by the provider */
|
|
39
|
+
errorDescription: string | null;
|
|
40
|
+
/** Raw provider or exchange error message */
|
|
41
|
+
message: string;
|
|
42
|
+
/** Product-neutral fallback message selected by the handler */
|
|
43
|
+
defaultMessage: string;
|
|
44
|
+
/** Callback phase that produced the error */
|
|
45
|
+
source: AuthCallbackErrorSource;
|
|
46
|
+
}
|
|
47
|
+
type AuthCallbackLinkingErrorMessageResolver = (context: AuthCallbackLinkingErrorContext) => string | Promise<string>;
|
|
48
|
+
interface AuthCallbackConfig {
|
|
49
|
+
/** Async Supabase client factory (for server-side) */
|
|
50
|
+
createClient: () => Promise<SupabaseClient>;
|
|
51
|
+
/** Default redirect path after successful auth */
|
|
52
|
+
defaultRedirect?: string;
|
|
53
|
+
/** Sign-in page path for error redirects */
|
|
54
|
+
signInPath?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Optional hook that runs after a successful auth callback and getUser().
|
|
57
|
+
* Use this for app-local profile hydration or other session-adjacent setup.
|
|
58
|
+
*/
|
|
59
|
+
postAuthHook?: AuthCallbackHook;
|
|
60
|
+
/**
|
|
61
|
+
* How to handle postAuthHook failures.
|
|
62
|
+
* "block" redirects to signInPath with a generic setup error.
|
|
63
|
+
* "ignore" allows the normal success redirect to continue.
|
|
64
|
+
*/
|
|
65
|
+
postAuthHookErrorMode?: AuthCallbackHookErrorMode;
|
|
66
|
+
/**
|
|
67
|
+
* Optional resolver for app-specific success redirects after auth succeeds.
|
|
68
|
+
* Use this for product-owned routing such as admin approval or subdomains.
|
|
69
|
+
*/
|
|
70
|
+
resolveSuccessRedirect?: AuthCallbackSuccessRedirectResolver;
|
|
71
|
+
/**
|
|
72
|
+
* Optional detector for deciding whether an auth error came from account
|
|
73
|
+
* linking. Defaults to common profile/settings redirect paths.
|
|
74
|
+
*/
|
|
75
|
+
isLinkingFlow?: AuthCallbackLinkingFlowDetector;
|
|
76
|
+
/**
|
|
77
|
+
* Optional resolver for product-specific account-linking error copy.
|
|
78
|
+
* Defaults remain product-neutral.
|
|
79
|
+
*/
|
|
80
|
+
resolveLinkingErrorMessage?: AuthCallbackLinkingErrorMessageResolver;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* createAuthCallbackHandler - Factory for OAuth/email callback route handler
|
|
84
|
+
*
|
|
85
|
+
* Creates a GET handler that exchanges authorization codes or verifies
|
|
86
|
+
* token_hash OTP links for sessions.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* // app/(auth)/callback/route.ts
|
|
91
|
+
* import { createAuthCallbackHandler } from "@caffeinebounce/identity";
|
|
92
|
+
* import { createClient } from "@/lib/supabase/server";
|
|
93
|
+
*
|
|
94
|
+
* export const GET = createAuthCallbackHandler({
|
|
95
|
+
* createClient,
|
|
96
|
+
* defaultRedirect: "/dashboard",
|
|
97
|
+
* postAuthHook: async ({ user }) => {
|
|
98
|
+
* await ensureProfileRecord({
|
|
99
|
+
* userId: user.id,
|
|
100
|
+
* email: user.email ?? null,
|
|
101
|
+
* metadata: user.user_metadata,
|
|
102
|
+
* });
|
|
103
|
+
* },
|
|
104
|
+
* postAuthHookErrorMode: "ignore",
|
|
105
|
+
* });
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
declare function createAuthCallbackHandler({ createClient, defaultRedirect, signInPath, postAuthHook, postAuthHookErrorMode, resolveSuccessRedirect, isLinkingFlow, resolveLinkingErrorMessage, }: AuthCallbackConfig): (request: Request) => Promise<Response>;
|
|
109
|
+
|
|
110
|
+
export { type AuthCallbackConfig, type AuthCallbackErrorSource, type AuthCallbackFlow, type AuthCallbackHook, type AuthCallbackHookContext, type AuthCallbackHookErrorMode, type AuthCallbackLinkingErrorContext, type AuthCallbackLinkingErrorMessageResolver, type AuthCallbackLinkingFlowContext, type AuthCallbackLinkingFlowDetector, type AuthCallbackRedirectTarget, type AuthCallbackSuccessRedirectResolver, createAuthCallbackHandler };
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var server = require('next/server');
|
|
4
|
+
var sharedUtils = require('@caffeinebounce/shared-utils');
|
|
5
|
+
|
|
6
|
+
// src/handlers/callback.ts
|
|
7
|
+
var POST_AUTH_HOOK_ERROR_MESSAGE = "Authentication completed, but setup failed. Please try again.";
|
|
8
|
+
var DEFAULT_LINKING_ACCOUNT_ERROR_MESSAGE = "This account is already connected to another account. Each external account can only be linked to one account.";
|
|
9
|
+
var EMAIL_OTP_TYPES = [
|
|
10
|
+
"signup",
|
|
11
|
+
"invite",
|
|
12
|
+
"magiclink",
|
|
13
|
+
"recovery",
|
|
14
|
+
"email_change",
|
|
15
|
+
"email"
|
|
16
|
+
];
|
|
17
|
+
function getSafeRedirectPath(candidate, fallback) {
|
|
18
|
+
if (candidate?.startsWith("/") && !candidate.startsWith("//")) {
|
|
19
|
+
return candidate;
|
|
20
|
+
}
|
|
21
|
+
return fallback;
|
|
22
|
+
}
|
|
23
|
+
function getEmailOtpType(value) {
|
|
24
|
+
if (!value) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return EMAIL_OTP_TYPES.includes(value) ? value : null;
|
|
28
|
+
}
|
|
29
|
+
function isDefaultLinkingFlow({
|
|
30
|
+
redirectPath
|
|
31
|
+
}) {
|
|
32
|
+
return redirectPath.includes("/profile") || redirectPath.includes("/settings");
|
|
33
|
+
}
|
|
34
|
+
function isAlreadyLinkedAccountError(message) {
|
|
35
|
+
return message.includes("already linked") || message.includes("identity already exists") || message.includes("already registered");
|
|
36
|
+
}
|
|
37
|
+
function getDefaultLinkingErrorMessage(message) {
|
|
38
|
+
return isAlreadyLinkedAccountError(message) ? DEFAULT_LINKING_ACCOUNT_ERROR_MESSAGE : message;
|
|
39
|
+
}
|
|
40
|
+
async function createLinkingErrorRedirect({
|
|
41
|
+
request,
|
|
42
|
+
origin,
|
|
43
|
+
redirectPath,
|
|
44
|
+
error,
|
|
45
|
+
errorDescription,
|
|
46
|
+
message,
|
|
47
|
+
source,
|
|
48
|
+
isLinkingFlow,
|
|
49
|
+
resolveLinkingErrorMessage
|
|
50
|
+
}) {
|
|
51
|
+
const linkingFlowContext = { request, origin, redirectPath };
|
|
52
|
+
if (!isLinkingFlow(linkingFlowContext)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const defaultMessage = getDefaultLinkingErrorMessage(message);
|
|
56
|
+
const userMessage = await resolveLinkingErrorMessage?.({
|
|
57
|
+
...linkingFlowContext,
|
|
58
|
+
error,
|
|
59
|
+
errorDescription,
|
|
60
|
+
message,
|
|
61
|
+
defaultMessage,
|
|
62
|
+
source
|
|
63
|
+
}) ?? defaultMessage;
|
|
64
|
+
const nextUrl = new URL(redirectPath, origin);
|
|
65
|
+
nextUrl.searchParams.set("link_error", userMessage);
|
|
66
|
+
return server.NextResponse.redirect(nextUrl.toString());
|
|
67
|
+
}
|
|
68
|
+
async function runPostAuthHook({
|
|
69
|
+
postAuthHook,
|
|
70
|
+
postAuthHookErrorMode,
|
|
71
|
+
supabase,
|
|
72
|
+
user,
|
|
73
|
+
request,
|
|
74
|
+
origin,
|
|
75
|
+
redirectPath,
|
|
76
|
+
flow,
|
|
77
|
+
otpType,
|
|
78
|
+
signInPath
|
|
79
|
+
}) {
|
|
80
|
+
if (!postAuthHook) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
await postAuthHook({
|
|
85
|
+
supabase,
|
|
86
|
+
user,
|
|
87
|
+
request,
|
|
88
|
+
origin,
|
|
89
|
+
redirectPath,
|
|
90
|
+
flow,
|
|
91
|
+
otpType
|
|
92
|
+
});
|
|
93
|
+
return null;
|
|
94
|
+
} catch {
|
|
95
|
+
if (postAuthHookErrorMode === "ignore") {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return server.NextResponse.redirect(
|
|
99
|
+
`${origin}${signInPath}?error=${encodeURIComponent(POST_AUTH_HOOK_ERROR_MESSAGE)}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function createRedirectResponse(target, origin) {
|
|
104
|
+
if (!target) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
if (target instanceof Response) {
|
|
108
|
+
return target;
|
|
109
|
+
}
|
|
110
|
+
if (target instanceof URL) {
|
|
111
|
+
return server.NextResponse.redirect(target.toString());
|
|
112
|
+
}
|
|
113
|
+
if (target.startsWith("/") && !target.startsWith("//")) {
|
|
114
|
+
return server.NextResponse.redirect(`${origin}${target}`);
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
async function redirectAfterSuccessfulAuth({
|
|
119
|
+
supabase,
|
|
120
|
+
request,
|
|
121
|
+
origin,
|
|
122
|
+
redirectPath,
|
|
123
|
+
flow,
|
|
124
|
+
otpType,
|
|
125
|
+
postAuthHook,
|
|
126
|
+
postAuthHookErrorMode,
|
|
127
|
+
signInPath,
|
|
128
|
+
resolveSuccessRedirect
|
|
129
|
+
}) {
|
|
130
|
+
const {
|
|
131
|
+
data: { user }
|
|
132
|
+
} = await supabase.auth.getUser();
|
|
133
|
+
if (!user) {
|
|
134
|
+
return server.NextResponse.redirect(`${origin}${redirectPath}`);
|
|
135
|
+
}
|
|
136
|
+
const postAuthHookRedirect = await runPostAuthHook({
|
|
137
|
+
postAuthHook,
|
|
138
|
+
postAuthHookErrorMode,
|
|
139
|
+
supabase,
|
|
140
|
+
user,
|
|
141
|
+
request,
|
|
142
|
+
origin,
|
|
143
|
+
redirectPath,
|
|
144
|
+
flow,
|
|
145
|
+
otpType,
|
|
146
|
+
signInPath
|
|
147
|
+
});
|
|
148
|
+
if (postAuthHookRedirect) {
|
|
149
|
+
return postAuthHookRedirect;
|
|
150
|
+
}
|
|
151
|
+
const customRedirect = await resolveSuccessRedirect?.({
|
|
152
|
+
supabase,
|
|
153
|
+
user,
|
|
154
|
+
request,
|
|
155
|
+
origin,
|
|
156
|
+
redirectPath,
|
|
157
|
+
flow,
|
|
158
|
+
otpType
|
|
159
|
+
});
|
|
160
|
+
const customRedirectResponse = createRedirectResponse(customRedirect, origin);
|
|
161
|
+
if (customRedirectResponse) {
|
|
162
|
+
return customRedirectResponse;
|
|
163
|
+
}
|
|
164
|
+
return server.NextResponse.redirect(`${origin}${redirectPath}`);
|
|
165
|
+
}
|
|
166
|
+
function createAuthCallbackHandler({
|
|
167
|
+
createClient,
|
|
168
|
+
defaultRedirect = "/dashboard",
|
|
169
|
+
signInPath = "/signin",
|
|
170
|
+
postAuthHook,
|
|
171
|
+
postAuthHookErrorMode = "block",
|
|
172
|
+
resolveSuccessRedirect,
|
|
173
|
+
isLinkingFlow = isDefaultLinkingFlow,
|
|
174
|
+
resolveLinkingErrorMessage
|
|
175
|
+
}) {
|
|
176
|
+
return async function GET(request) {
|
|
177
|
+
const requestUrl = new URL(request.url);
|
|
178
|
+
const code = requestUrl.searchParams.get("code");
|
|
179
|
+
const tokenHash = requestUrl.searchParams.get("token_hash");
|
|
180
|
+
const rawOtpType = requestUrl.searchParams.get("type");
|
|
181
|
+
const otpType = getEmailOtpType(rawOtpType);
|
|
182
|
+
const next = getSafeRedirectPath(
|
|
183
|
+
requestUrl.searchParams.get("next") ?? requestUrl.searchParams.get("redirect_to"),
|
|
184
|
+
defaultRedirect
|
|
185
|
+
);
|
|
186
|
+
const origin = requestUrl.origin;
|
|
187
|
+
const error = requestUrl.searchParams.get("error");
|
|
188
|
+
const errorDescription = requestUrl.searchParams.get("error_description");
|
|
189
|
+
if (error) {
|
|
190
|
+
const message = errorDescription || error;
|
|
191
|
+
const linkingErrorRedirect = await createLinkingErrorRedirect({
|
|
192
|
+
request,
|
|
193
|
+
origin,
|
|
194
|
+
redirectPath: next,
|
|
195
|
+
error,
|
|
196
|
+
errorDescription,
|
|
197
|
+
message,
|
|
198
|
+
source: "oauth_error",
|
|
199
|
+
isLinkingFlow,
|
|
200
|
+
resolveLinkingErrorMessage
|
|
201
|
+
});
|
|
202
|
+
if (linkingErrorRedirect) {
|
|
203
|
+
return linkingErrorRedirect;
|
|
204
|
+
}
|
|
205
|
+
return server.NextResponse.redirect(
|
|
206
|
+
`${origin}${signInPath}?error=${encodeURIComponent(message)}`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (code) {
|
|
210
|
+
const supabase = await createClient();
|
|
211
|
+
const { error: exchangeError } = await supabase.auth.exchangeCodeForSession(code);
|
|
212
|
+
if (!exchangeError) {
|
|
213
|
+
return redirectAfterSuccessfulAuth({
|
|
214
|
+
supabase,
|
|
215
|
+
request,
|
|
216
|
+
origin,
|
|
217
|
+
redirectPath: next,
|
|
218
|
+
flow: "oauth",
|
|
219
|
+
postAuthHook,
|
|
220
|
+
postAuthHookErrorMode,
|
|
221
|
+
signInPath,
|
|
222
|
+
resolveSuccessRedirect
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
const errorMessage = exchangeError.message;
|
|
226
|
+
const linkingErrorRedirect = await createLinkingErrorRedirect({
|
|
227
|
+
request,
|
|
228
|
+
origin,
|
|
229
|
+
redirectPath: next,
|
|
230
|
+
error: exchangeError.name || "code_exchange",
|
|
231
|
+
errorDescription: null,
|
|
232
|
+
message: errorMessage,
|
|
233
|
+
source: "code_exchange",
|
|
234
|
+
isLinkingFlow,
|
|
235
|
+
resolveLinkingErrorMessage
|
|
236
|
+
});
|
|
237
|
+
if (linkingErrorRedirect) {
|
|
238
|
+
return linkingErrorRedirect;
|
|
239
|
+
}
|
|
240
|
+
return server.NextResponse.redirect(
|
|
241
|
+
`${origin}${signInPath}?error=${encodeURIComponent(errorMessage)}`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
if (tokenHash || rawOtpType) {
|
|
245
|
+
if (!tokenHash || !otpType) {
|
|
246
|
+
return server.NextResponse.redirect(
|
|
247
|
+
`${origin}${signInPath}?error=${encodeURIComponent("Invalid verification link")}`
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const supabase = await createClient();
|
|
251
|
+
const { error: verifyError } = await supabase.auth.verifyOtp({
|
|
252
|
+
token_hash: tokenHash,
|
|
253
|
+
type: otpType
|
|
254
|
+
});
|
|
255
|
+
if (!verifyError) {
|
|
256
|
+
return redirectAfterSuccessfulAuth({
|
|
257
|
+
supabase,
|
|
258
|
+
request,
|
|
259
|
+
origin,
|
|
260
|
+
redirectPath: next,
|
|
261
|
+
flow: "otp",
|
|
262
|
+
otpType,
|
|
263
|
+
postAuthHook,
|
|
264
|
+
postAuthHookErrorMode,
|
|
265
|
+
signInPath,
|
|
266
|
+
resolveSuccessRedirect
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return server.NextResponse.redirect(
|
|
270
|
+
`${origin}${signInPath}?error=${encodeURIComponent(verifyError.message)}`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
return server.NextResponse.redirect(
|
|
274
|
+
`${origin}${signInPath}?error=No authorization code received`
|
|
275
|
+
);
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
Object.defineProperty(exports, "generateSecureToken", {
|
|
280
|
+
enumerable: true,
|
|
281
|
+
get: function () { return sharedUtils.generateSecureToken; }
|
|
282
|
+
});
|
|
283
|
+
Object.defineProperty(exports, "getClientIP", {
|
|
284
|
+
enumerable: true,
|
|
285
|
+
get: function () { return sharedUtils.getClientIP; }
|
|
286
|
+
});
|
|
287
|
+
Object.defineProperty(exports, "getGeolocationFromIP", {
|
|
288
|
+
enumerable: true,
|
|
289
|
+
get: function () { return sharedUtils.getGeolocationFromIP; }
|
|
290
|
+
});
|
|
291
|
+
Object.defineProperty(exports, "hashString", {
|
|
292
|
+
enumerable: true,
|
|
293
|
+
get: function () { return sharedUtils.hashString; }
|
|
294
|
+
});
|
|
295
|
+
exports.createAuthCallbackHandler = createAuthCallbackHandler;
|