@tern-secure/auth 1.1.0-canary.v20251020170039 → 1.1.0-canary.v20251024005655

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 (43) hide show
  1. package/dist/cjs/index.js +3 -0
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/instance/TernAuth.js +78 -136
  4. package/dist/cjs/instance/TernAuth.js.map +1 -1
  5. package/dist/cjs/resources/SignIn.js +78 -36
  6. package/dist/cjs/resources/SignIn.js.map +1 -1
  7. package/dist/cjs/utils/construct.js +91 -4
  8. package/dist/cjs/utils/construct.js.map +1 -1
  9. package/dist/cjs/utils/index.js +5 -1
  10. package/dist/cjs/utils/index.js.map +1 -1
  11. package/dist/cjs/utils/redirectUrls.js +156 -0
  12. package/dist/cjs/utils/redirectUrls.js.map +1 -0
  13. package/dist/cjs/utils/windowNavigate.js +45 -0
  14. package/dist/cjs/utils/windowNavigate.js.map +1 -0
  15. package/dist/esm/index.js +2 -0
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/instance/TernAuth.js +79 -136
  18. package/dist/esm/instance/TernAuth.js.map +1 -1
  19. package/dist/esm/resources/SignIn.js +78 -36
  20. package/dist/esm/resources/SignIn.js.map +1 -1
  21. package/dist/esm/utils/construct.js +84 -4
  22. package/dist/esm/utils/construct.js.map +1 -1
  23. package/dist/esm/utils/index.js +2 -0
  24. package/dist/esm/utils/index.js.map +1 -1
  25. package/dist/esm/utils/redirectUrls.js +132 -0
  26. package/dist/esm/utils/redirectUrls.js.map +1 -0
  27. package/dist/esm/utils/windowNavigate.js +19 -0
  28. package/dist/esm/utils/windowNavigate.js.map +1 -0
  29. package/dist/types/index.d.ts +2 -1
  30. package/dist/types/index.d.ts.map +1 -1
  31. package/dist/types/instance/TernAuth.d.ts +10 -10
  32. package/dist/types/instance/TernAuth.d.ts.map +1 -1
  33. package/dist/types/resources/SignIn.d.ts +26 -5
  34. package/dist/types/resources/SignIn.d.ts.map +1 -1
  35. package/dist/types/utils/construct.d.ts +31 -0
  36. package/dist/types/utils/construct.d.ts.map +1 -1
  37. package/dist/types/utils/index.d.ts +2 -0
  38. package/dist/types/utils/index.d.ts.map +1 -1
  39. package/dist/types/utils/redirectUrls.d.ts +23 -0
  40. package/dist/types/utils/redirectUrls.d.ts.map +1 -0
  41. package/dist/types/utils/windowNavigate.d.ts +12 -0
  42. package/dist/types/utils/windowNavigate.d.ts.map +1 -0
  43. package/package.json +3 -3
@@ -26,7 +26,7 @@ var import_auth = require("firebase/auth");
26
26
  var import_Base = require("./Base");
27
27
  class SignIn extends import_Base.TernSecureBase {
28
28
  pathRoot = "/sessions/createsession";
29
- status;
29
+ status = null;
30
30
  auth;
31
31
  csrfToken;
32
32
  _currentUser = null;
@@ -81,24 +81,17 @@ class SignIn extends import_Base.TernSecureBase {
81
81
  console.error(authError);
82
82
  }
83
83
  };
84
- withSocialProvider = async (provider, options) => {
84
+ withSocialProvider = async (provider, options = {}) => {
85
85
  try {
86
- if ((options == null ? void 0 : options.mode) === "redirect") {
86
+ const { mode = "popup" } = options;
87
+ if (mode === "redirect") {
87
88
  const redirectResult = await this.authRedirectResult();
88
89
  if (redirectResult) {
89
- if (redirectResult.status === "success") {
90
- console.log("Redirect after sign in");
91
- }
92
90
  return redirectResult;
93
91
  }
94
- await this._signInWithRedirect(provider);
95
- return;
92
+ return await this._signInWithRedirect(provider, options);
96
93
  } else {
97
- await this._signInWithPopUp(provider);
98
- return {
99
- status: "success",
100
- message: "Sign in successful"
101
- };
94
+ return await this._signInWithPopUp(provider, options);
102
95
  }
103
96
  } catch (error) {
104
97
  return {
@@ -139,22 +132,27 @@ class SignIn extends import_Base.TernSecureBase {
139
132
  switch (providerName.toLowerCase()) {
140
133
  case "google": {
141
134
  const googleProvider = new import_auth.GoogleAuthProvider();
142
- return {
143
- provider: googleProvider,
144
- customParameters: {
145
- login_hint: "user@example.com",
146
- prompt: "select_account"
147
- }
148
- };
135
+ return { provider: googleProvider };
136
+ }
137
+ case "apple": {
138
+ const appleProvider = new import_auth.OAuthProvider("apple.com");
139
+ return { provider: appleProvider };
149
140
  }
150
141
  case "microsoft": {
151
142
  const microsoftProvider = new import_auth.OAuthProvider("microsoft.com");
152
- return {
153
- provider: microsoftProvider,
154
- customParameters: {
155
- prompt: "consent"
156
- }
157
- };
143
+ return { provider: microsoftProvider };
144
+ }
145
+ case "github": {
146
+ const githubProvider = new import_auth.OAuthProvider("github.com");
147
+ return { provider: githubProvider };
148
+ }
149
+ case "twitter": {
150
+ const twitterProvider = new import_auth.OAuthProvider("twitter.com");
151
+ return { provider: twitterProvider };
152
+ }
153
+ case "facebook": {
154
+ const facebookProvider = new import_auth.OAuthProvider("facebook.com");
155
+ return { provider: facebookProvider };
158
156
  }
159
157
  default:
160
158
  throw new Error(`Unsupported provider: ${providerName}`);
@@ -182,12 +180,56 @@ class SignIn extends import_Base.TernSecureBase {
182
180
  };
183
181
  }
184
182
  }
185
- async executeAuthMethod(authMethod, providerName) {
186
- const config = this.getProviderConfig(providerName);
187
- config.provider.setCustomParameters(config.customParameters);
183
+ /**
184
+ * Sets custom OAuth parameters on the provider if provided by consumer
185
+ * @param provider - Firebase auth provider instance
186
+ * @param customParameters - Consumer-provided OAuth parameters
187
+ */
188
+ setProviderCustomParameters(provider, customParameters) {
189
+ if (!customParameters || Object.keys(customParameters).length === 0) {
190
+ return;
191
+ }
192
+ provider.setCustomParameters(customParameters);
193
+ }
194
+ /**
195
+ * Adds OAuth scopes to the provider if provided by consumer
196
+ * Handles provider-specific scope setting logic
197
+ * @param provider - Firebase auth provider instance
198
+ * @param scopes - Array of OAuth scopes to request
199
+ */
200
+ setProviderScopes(provider, scopes) {
201
+ if (!scopes || scopes.length === 0) {
202
+ return;
203
+ }
204
+ if (provider instanceof import_auth.GoogleAuthProvider) {
205
+ scopes.forEach((scope) => {
206
+ provider.addScope(scope);
207
+ });
208
+ } else if (provider instanceof import_auth.OAuthProvider) {
209
+ provider.addScope(scopes.join(" "));
210
+ }
211
+ }
212
+ /**
213
+ * Configures OAuth provider with consumer-provided options
214
+ * @param provider - Firebase auth provider instance
215
+ * @param options - Consumer options containing custom parameters and scopes
216
+ */
217
+ configureProvider(provider, options) {
218
+ this.setProviderCustomParameters(provider, options.customParameters);
219
+ this.setProviderScopes(provider, options.scopes);
220
+ }
221
+ executeAuthMethod = async (authMethod, providerName, options = {}) => {
188
222
  try {
189
- await authMethod(this.auth, config.provider);
190
- return { status: "success", message: "Authentication initiated" };
223
+ const config = this.getProviderConfig(providerName);
224
+ this.configureProvider(config.provider, options);
225
+ const { user, providerId, operationType } = await authMethod(this.auth, config.provider);
226
+ return {
227
+ status: "success",
228
+ message: "Authentication successful",
229
+ user,
230
+ providerId,
231
+ operationType
232
+ };
191
233
  } catch (error) {
192
234
  const authError = (0, import_errors.handleFirebaseAuthError)(error);
193
235
  return {
@@ -196,12 +238,12 @@ class SignIn extends import_Base.TernSecureBase {
196
238
  error: authError.code
197
239
  };
198
240
  }
241
+ };
242
+ async _signInWithRedirect(providerName, options = {}) {
243
+ return this.executeAuthMethod(import_auth.signInWithRedirect, providerName, options);
199
244
  }
200
- async _signInWithRedirect(providerName) {
201
- return this.executeAuthMethod(import_auth.signInWithRedirect, providerName);
202
- }
203
- async _signInWithPopUp(providerName) {
204
- return this.executeAuthMethod(import_auth.signInWithPopup, providerName);
245
+ async _signInWithPopUp(providerName, options = {}) {
246
+ return this.executeAuthMethod(import_auth.signInWithPopup, providerName, options);
205
247
  }
206
248
  async checkRedirectResult() {
207
249
  return this.authRedirectResult();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/resources/SignIn.ts"],"sourcesContent":["import { handleFirebaseAuthError } from '@tern-secure/shared/errors';\nimport type {\n ResendEmailVerification,\n SignInFormValues,\n SignInResource,\n SignInResponse as SignInResponseFromTypes,\n SignInStatus,\n TernSecureUser,\n} from '@tern-secure/types';\nimport type { Auth, UserCredential } from 'firebase/auth';\nimport {\n getRedirectResult,\n GoogleAuthProvider,\n OAuthProvider,\n sendEmailVerification,\n signInWithEmailAndPassword,\n signInWithPopup,\n signInWithRedirect,\n} from 'firebase/auth';\n\nimport { TernSecureBase } from './Base';\n\ntype SignInResponse = SignInResponseFromTypes;\n\ninterface ProviderConfig {\n provider: GoogleAuthProvider | OAuthProvider;\n customParameters: Record<string, string>;\n}\n\nexport type TernRequestInit = RequestInit;\n\nexport type SignInParams = {\n idToken: string;\n csrfToken: string | undefined;\n};\n\ntype FirebaseAuthResult = UserCredential | void;\n\ntype AuthMethodFunction = (\n auth: Auth,\n provider: GoogleAuthProvider | OAuthProvider,\n) => Promise<FirebaseAuthResult>;\n\nexport class SignIn extends TernSecureBase implements SignInResource {\n pathRoot = '/sessions/createsession';\n\n status?: SignInStatus | undefined;\n private auth: Auth;\n private csrfToken: string | undefined;\n private _currentUser: TernSecureUser | null = null;\n\n constructor(auth: Auth, csrfToken: string | undefined) {\n super();\n this.auth = auth;\n this.csrfToken = csrfToken;\n }\n\n signInWithCredential = async (credential: UserCredential) => {\n const idToken = await credential.user.getIdToken();\n const params = {\n idToken: idToken,\n csrfToken: this.csrfToken,\n };\n\n return this._post({\n path: this.pathRoot,\n body: params,\n });\n };\n\n withEmailAndPassword = async (params: SignInFormValues): Promise<SignInResponse> => {\n try {\n const { email, password } = params;\n const { user, providerId, operationType } = await signInWithEmailAndPassword(\n this.auth,\n email,\n password,\n );\n return {\n status: 'success',\n user,\n providerId,\n operationType,\n message: 'Authentication successful',\n error: !user.emailVerified ? 'REQUIRES_VERIFICATION' : 'AUTHENTICATED',\n };\n } catch (error) {\n const authError = handleFirebaseAuthError(error);\n return {\n status: 'error',\n message: authError.message,\n error: authError.code,\n };\n }\n };\n\n withCredential = async (params: SignInFormValues): Promise<void> => {\n try {\n const { email, password } = params;\n const userCredential = await signInWithEmailAndPassword(this.auth, email, password);\n await this.signInWithCredential(userCredential);\n } catch (error) {\n const authError = handleFirebaseAuthError(error);\n console.error(authError);\n }\n };\n\n withSocialProvider = async (\n provider: string,\n options?: {\n mode?: 'popup' | 'redirect';\n },\n ): Promise<SignInResponse | void> => {\n try {\n if (options?.mode === 'redirect') {\n const redirectResult = await this.authRedirectResult();\n\n if (redirectResult) {\n if (redirectResult.status === 'success') {\n console.log('Redirect after sign in');\n }\n return redirectResult;\n }\n\n await this._signInWithRedirect(provider);\n return;\n } else {\n await this._signInWithPopUp(provider);\n return {\n status: 'success',\n message: 'Sign in successful',\n };\n }\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || `Sign in with ${provider} failed`,\n error,\n };\n }\n };\n\n completeMfaSignIn = async (_mfaToken: string, _mfaContext?: any): Promise<SignInResponse> => {\n throw new Error('Method not implemented.');\n };\n\n sendPasswordResetEmail = async (email: string): Promise<void> => {\n console.log(`Sending password reset email to ${email}`);\n };\n\n resendEmailVerification = async (): Promise<ResendEmailVerification> => {\n const user = this._currentUser;\n if (!user) {\n throw new Error('No user is currently signed in');\n }\n\n await user.reload();\n\n if (user.emailVerified) {\n return {\n isVerified: true,\n };\n }\n\n const actionCodeSettings = {\n url: '/sign-in', // TODO: Make this configurable\n handleCodeInApp: true,\n };\n\n await sendEmailVerification(user, actionCodeSettings);\n return {\n isVerified: false,\n };\n };\n\n private getProviderConfig(providerName: string): ProviderConfig {\n switch (providerName.toLowerCase()) {\n case 'google': {\n const googleProvider = new GoogleAuthProvider();\n return {\n provider: googleProvider,\n customParameters: {\n login_hint: 'user@example.com',\n prompt: 'select_account',\n },\n };\n }\n case 'microsoft': {\n const microsoftProvider = new OAuthProvider('microsoft.com');\n return {\n provider: microsoftProvider,\n customParameters: {\n prompt: 'consent',\n },\n };\n }\n default:\n throw new Error(`Unsupported provider: ${providerName}`);\n }\n }\n\n private async authRedirectResult(): Promise<SignInResponse | null> {\n try {\n const result = await getRedirectResult(this.auth);\n\n if (result) {\n const { user, providerId, operationType } = result;\n return {\n status: 'success',\n user,\n providerId,\n operationType,\n };\n }\n return null;\n } catch (error) {\n const authError = handleFirebaseAuthError(error);\n return {\n status: 'error',\n message: authError.message,\n error: authError.code,\n };\n }\n }\n\n private async executeAuthMethod(\n authMethod: AuthMethodFunction,\n providerName: string,\n ): Promise<SignInResponse> {\n const config = this.getProviderConfig(providerName);\n config.provider.setCustomParameters(config.customParameters);\n try {\n await authMethod(this.auth, config.provider);\n return { status: 'success', message: 'Authentication initiated' };\n } catch (error) {\n const authError = handleFirebaseAuthError(error);\n return {\n status: 'error',\n message: authError.message,\n error: authError.code,\n };\n }\n }\n\n private async _signInWithRedirect(providerName: string): Promise<SignInResponse> {\n return this.executeAuthMethod(signInWithRedirect, providerName);\n }\n\n private async _signInWithPopUp(providerName: string): Promise<SignInResponse> {\n return this.executeAuthMethod(signInWithPopup, providerName);\n }\n\n public async checkRedirectResult(): Promise<SignInResponse | null> {\n return this.authRedirectResult();\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/resources/SignIn.ts"],"sourcesContent":["import { handleFirebaseAuthError } from '@tern-secure/shared/errors';\nimport type {\n ResendEmailVerification,\n SignInFormValues,\n SignInResource,\n SignInResponse as SignInResponseFromTypes,\n SignInStatus,\n SocialProviderOptions,\n TernSecureUser,\n} from '@tern-secure/types';\nimport type { Auth, UserCredential } from 'firebase/auth';\nimport {\n getRedirectResult,\n GoogleAuthProvider,\n OAuthProvider,\n sendEmailVerification,\n signInWithEmailAndPassword,\n signInWithPopup,\n signInWithRedirect,\n} from 'firebase/auth';\n\nimport { TernSecureBase } from './Base';\n\ntype SignInResponse = SignInResponseFromTypes;\n\ninterface ProviderConfig {\n provider: GoogleAuthProvider | OAuthProvider;\n //customParameters: Record<string, string>;\n}\n\nexport type TernRequestInit = RequestInit;\n\nexport type SignInParams = {\n idToken: string;\n csrfToken: string | undefined;\n};\n\ntype FirebaseAuthResult = UserCredential;\n\ntype AuthMethodFunction = (\n auth: Auth,\n provider: GoogleAuthProvider | OAuthProvider,\n) => Promise<FirebaseAuthResult>;\n\n/**\n * Supported OAuth providers\n */\nexport type SupportedProvider =\n | 'google'\n | 'apple'\n | 'microsoft'\n | 'github'\n | 'twitter'\n | 'facebook'\n | string; // Allow custom providers like 'custom.provider.com'\n\nexport class SignIn extends TernSecureBase implements SignInResource {\n pathRoot = '/sessions/createsession';\n\n status: SignInStatus | null = null;\n private auth: Auth;\n private csrfToken: string | undefined;\n private _currentUser: TernSecureUser | null = null;\n\n constructor(auth: Auth, csrfToken: string | undefined) {\n super();\n this.auth = auth;\n this.csrfToken = csrfToken;\n }\n\n signInWithCredential = async (credential: UserCredential) => {\n const idToken = await credential.user.getIdToken();\n const params = {\n idToken: idToken,\n csrfToken: this.csrfToken,\n };\n\n return this._post({\n path: this.pathRoot,\n body: params,\n });\n };\n\n withEmailAndPassword = async (params: SignInFormValues): Promise<SignInResponse> => {\n try {\n const { email, password } = params;\n const { user, providerId, operationType } = await signInWithEmailAndPassword(\n this.auth,\n email,\n password,\n );\n return {\n status: 'success',\n user,\n providerId,\n operationType,\n message: 'Authentication successful',\n error: !user.emailVerified ? 'REQUIRES_VERIFICATION' : 'AUTHENTICATED',\n };\n } catch (error) {\n const authError = handleFirebaseAuthError(error);\n return {\n status: 'error',\n message: authError.message,\n error: authError.code,\n };\n }\n };\n\n withCredential = async (params: SignInFormValues): Promise<void> => {\n try {\n const { email, password } = params;\n const userCredential = await signInWithEmailAndPassword(this.auth, email, password);\n await this.signInWithCredential(userCredential);\n } catch (error) {\n const authError = handleFirebaseAuthError(error);\n console.error(authError);\n }\n };\n\n withSocialProvider = async (\n provider: SupportedProvider,\n options: SocialProviderOptions = {},\n ): Promise<SignInResponse> => {\n try {\n const { mode = 'popup' } = options;\n if (mode === 'redirect') {\n const redirectResult = await this.authRedirectResult();\n\n if (redirectResult) {\n return redirectResult;\n }\n\n return await this._signInWithRedirect(provider, options);\n } else {\n return await this._signInWithPopUp(provider, options);\n }\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || `Sign in with ${provider} failed`,\n error,\n };\n }\n };\n\n completeMfaSignIn = async (_mfaToken: string, _mfaContext?: any): Promise<SignInResponse> => {\n throw new Error('Method not implemented.');\n };\n\n sendPasswordResetEmail = async (email: string): Promise<void> => {\n console.log(`Sending password reset email to ${email}`);\n };\n\n resendEmailVerification = async (): Promise<ResendEmailVerification> => {\n const user = this._currentUser;\n if (!user) {\n throw new Error('No user is currently signed in');\n }\n\n await user.reload();\n\n if (user.emailVerified) {\n return {\n isVerified: true,\n };\n }\n\n const actionCodeSettings = {\n url: '/sign-in', // TODO: Make this configurable\n handleCodeInApp: true,\n };\n\n await sendEmailVerification(user, actionCodeSettings);\n return {\n isVerified: false,\n };\n };\n\n private getProviderConfig(providerName: SupportedProvider): ProviderConfig {\n switch (providerName.toLowerCase()) {\n case 'google': {\n const googleProvider = new GoogleAuthProvider();\n return { provider: googleProvider };\n }\n case 'apple': {\n const appleProvider = new OAuthProvider('apple.com');\n return { provider: appleProvider };\n }\n case 'microsoft': {\n const microsoftProvider = new OAuthProvider('microsoft.com');\n return { provider: microsoftProvider };\n }\n case 'github': {\n const githubProvider = new OAuthProvider('github.com');\n return { provider: githubProvider };\n }\n case 'twitter': {\n const twitterProvider = new OAuthProvider('twitter.com');\n return { provider: twitterProvider };\n }\n case 'facebook': {\n const facebookProvider = new OAuthProvider('facebook.com');\n return { provider: facebookProvider };\n }\n default:\n throw new Error(`Unsupported provider: ${providerName}`);\n }\n }\n\n private async authRedirectResult(): Promise<SignInResponse | null> {\n try {\n const result = await getRedirectResult(this.auth);\n\n if (result) {\n const { user, providerId, operationType } = result;\n return {\n status: 'success',\n user,\n providerId,\n operationType,\n };\n }\n return null;\n } catch (error) {\n const authError = handleFirebaseAuthError(error);\n return {\n status: 'error',\n message: authError.message,\n error: authError.code,\n };\n }\n }\n\n /**\n * Sets custom OAuth parameters on the provider if provided by consumer\n * @param provider - Firebase auth provider instance\n * @param customParameters - Consumer-provided OAuth parameters\n */\n private setProviderCustomParameters(\n provider: GoogleAuthProvider | OAuthProvider,\n customParameters?: Record<string, string>,\n ): void {\n if (!customParameters || Object.keys(customParameters).length === 0) {\n return;\n }\n\n provider.setCustomParameters(customParameters);\n }\n\n /**\n * Adds OAuth scopes to the provider if provided by consumer\n * Handles provider-specific scope setting logic\n * @param provider - Firebase auth provider instance\n * @param scopes - Array of OAuth scopes to request\n */\n private setProviderScopes(provider: GoogleAuthProvider | OAuthProvider, scopes?: string[]): void {\n if (!scopes || scopes.length === 0) {\n return;\n }\n\n if (provider instanceof GoogleAuthProvider) {\n // Google provider supports individual scope addition\n scopes.forEach(scope => {\n (provider as GoogleAuthProvider).addScope(scope);\n });\n } else if (provider instanceof OAuthProvider) {\n // OAuth providers expect space-separated scope string\n (provider as OAuthProvider).addScope(scopes.join(' '));\n }\n }\n\n /**\n * Configures OAuth provider with consumer-provided options\n * @param provider - Firebase auth provider instance\n * @param options - Consumer options containing custom parameters and scopes\n */\n private configureProvider(\n provider: GoogleAuthProvider | OAuthProvider,\n options: SocialProviderOptions,\n ): void {\n this.setProviderCustomParameters(provider, options.customParameters);\n this.setProviderScopes(provider, options.scopes);\n }\n\n private executeAuthMethod = async (\n authMethod: AuthMethodFunction,\n providerName: SupportedProvider,\n options: SocialProviderOptions = {},\n ): Promise<SignInResponse> => {\n try {\n const config = this.getProviderConfig(providerName);\n\n this.configureProvider(config.provider, options);\n\n const { user, providerId, operationType } = await authMethod(this.auth, config.provider);\n\n return {\n status: 'success',\n message: 'Authentication successful',\n user,\n providerId,\n operationType,\n };\n } catch (error) {\n const authError = handleFirebaseAuthError(error);\n return {\n status: 'error',\n message: authError.message,\n error: authError.code,\n };\n }\n };\n\n private async _signInWithRedirect(\n providerName: SupportedProvider,\n options: SocialProviderOptions = {},\n ): Promise<SignInResponse> {\n return this.executeAuthMethod(signInWithRedirect, providerName, options);\n }\n\n private async _signInWithPopUp(\n providerName: SupportedProvider,\n options: SocialProviderOptions = {},\n ): Promise<SignInResponse> {\n return this.executeAuthMethod(signInWithPopup, providerName, options);\n }\n\n public async checkRedirectResult(): Promise<SignInResponse | null> {\n return this.authRedirectResult();\n }\n}\n"],"mappings":"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","names":[]}
@@ -22,19 +22,28 @@ __export(construct_exports, {
22
22
  constructFullUrl: () => constructFullUrl,
23
23
  getPreviousPath: () => getPreviousPath,
24
24
  getValidRedirectUrl: () => getValidRedirectUrl,
25
+ hasBannedProtocol: () => hasBannedProtocol,
25
26
  hasRedirectLoop: () => hasRedirectLoop,
27
+ isAllowedRedirect: () => isAllowedRedirect,
28
+ isProblematicUrl: () => isProblematicUrl,
29
+ isValidUrl: () => isValidUrl,
30
+ relativeToAbsoluteUrl: () => relativeToAbsoluteUrl,
26
31
  storePreviousPath: () => storePreviousPath,
32
+ stripOrigin: () => stripOrigin,
27
33
  toURL: () => toURL,
34
+ trimTrailingSlash: () => trimTrailingSlash,
28
35
  urlWithRedirect: () => urlWithRedirect
29
36
  });
30
37
  module.exports = __toCommonJS(construct_exports);
31
38
  var import_caseUtils = require("@tern-secure/shared/caseUtils");
39
+ var import_globs = require("@tern-secure/shared/globs");
40
+ var import_logger = require("@tern-secure/shared/logger");
32
41
  var import_path = require("./path");
33
42
  var import_querystring = require("./querystring");
34
43
  const DUMMY_URL_BASE = "http://ternsecure-dummy";
44
+ const BANNED_URI_PROTOCOLS = ["javascript:"];
35
45
  function buildURL(params, options = {}) {
36
46
  const { base, hashPath, hashSearch, searchParams, hashSearchParams, ...rest } = params;
37
- const { stringify, skipOrigin } = options;
38
47
  let baseFallback = "";
39
48
  if (typeof window !== "undefined" && !!window.location) {
40
49
  baseFallback = window.location.href;
@@ -45,7 +54,7 @@ function buildURL(params, options = {}) {
45
54
  if (searchParams instanceof URLSearchParams) {
46
55
  searchParams.forEach((value, key) => {
47
56
  if (value !== null && value !== void 0) {
48
- url.searchParams.set(key, value);
57
+ url.searchParams.set((0, import_caseUtils.camelToSnake)(key), value);
49
58
  }
50
59
  });
51
60
  }
@@ -57,8 +66,6 @@ function buildURL(params, options = {}) {
57
66
  for (const [key, val] of Object.entries(searchParamsFromHashSearchString)) {
58
67
  dummyUrlForHash.searchParams.append(key, val);
59
68
  }
60
- const finalHashPath = hashPath || "";
61
- const queryForHash = new URLSearchParams(hashSearch || "");
62
69
  if (hashSearchParams) {
63
70
  const paramsArr = Array.isArray(hashSearchParams) ? hashSearchParams : [hashSearchParams];
64
71
  for (const _params of paramsArr) {
@@ -78,6 +85,7 @@ function buildURL(params, options = {}) {
78
85
  url.hash = newHash;
79
86
  }
80
87
  }
88
+ const { stringify, skipOrigin } = options;
81
89
  if (stringify) {
82
90
  return skipOrigin ? url.href.replace(url.origin, "") : url.href;
83
91
  }
@@ -152,15 +160,94 @@ const validateUrl = (url) => {
152
160
  function toURL(url) {
153
161
  return new URL(url.toString(), window.location.origin);
154
162
  }
163
+ function stripOrigin(url) {
164
+ url = toURL(url);
165
+ return url.href.replace(url.origin, "");
166
+ }
167
+ const trimTrailingSlash = (path) => {
168
+ return (path || "").replace(/\/+$/, "");
169
+ };
170
+ function isValidUrl(val) {
171
+ if (!val) {
172
+ return false;
173
+ }
174
+ try {
175
+ new URL(val);
176
+ return true;
177
+ } catch {
178
+ return false;
179
+ }
180
+ }
181
+ function relativeToAbsoluteUrl(url, origin) {
182
+ try {
183
+ return new URL(url);
184
+ } catch {
185
+ return new URL(url, origin);
186
+ }
187
+ }
188
+ const disallowedPatterns = [
189
+ /\0/,
190
+ // Null bytes
191
+ /^\/\//,
192
+ // Protocol-relative
193
+ // eslint-disable-next-line no-control-regex
194
+ /[\x00-\x1F]/
195
+ // Control characters
196
+ ];
197
+ function isProblematicUrl(url) {
198
+ if (hasBannedProtocol(url)) {
199
+ return true;
200
+ }
201
+ for (const pattern of disallowedPatterns) {
202
+ if (pattern.test(url.pathname)) {
203
+ return true;
204
+ }
205
+ }
206
+ return false;
207
+ }
208
+ function hasBannedProtocol(val) {
209
+ if (!isValidUrl(val)) {
210
+ return false;
211
+ }
212
+ const protocol = new URL(val).protocol;
213
+ return BANNED_URI_PROTOCOLS.some((bp) => bp === protocol);
214
+ }
215
+ const isAllowedRedirect = (allowedRedirectOrigins, currentOrigin) => (_url) => {
216
+ if (!currentOrigin) return true;
217
+ let url = _url;
218
+ if (typeof url === "string") {
219
+ url = relativeToAbsoluteUrl(url, currentOrigin);
220
+ }
221
+ if (!allowedRedirectOrigins) {
222
+ return true;
223
+ }
224
+ const isSameOrigin = currentOrigin === url.origin;
225
+ const isAllowed = !isProblematicUrl(url) && (isSameOrigin || allowedRedirectOrigins.map(
226
+ (origin) => typeof origin === "string" ? import_globs.globs.toRegexp(trimTrailingSlash(origin)) : origin
227
+ ).some((origin) => origin.test(trimTrailingSlash(url.origin))));
228
+ if (!isAllowed) {
229
+ import_logger.logger.warnOnce(
230
+ `Clerk: Redirect URL ${url} is not on one of the allowedRedirectOrigins, falling back to the default redirect URL.`
231
+ );
232
+ }
233
+ return isAllowed;
234
+ };
155
235
  // Annotate the CommonJS export names for ESM import in node:
156
236
  0 && (module.exports = {
157
237
  buildURL,
158
238
  constructFullUrl,
159
239
  getPreviousPath,
160
240
  getValidRedirectUrl,
241
+ hasBannedProtocol,
161
242
  hasRedirectLoop,
243
+ isAllowedRedirect,
244
+ isProblematicUrl,
245
+ isValidUrl,
246
+ relativeToAbsoluteUrl,
162
247
  storePreviousPath,
248
+ stripOrigin,
163
249
  toURL,
250
+ trimTrailingSlash,
164
251
  urlWithRedirect
165
252
  });
166
253
  //# sourceMappingURL=construct.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/construct.ts"],"sourcesContent":["import { camelToSnake } from '@tern-secure/shared/caseUtils'\n\nimport { joinPaths } from './path';\nimport { getQueryParams } from './querystring';\n\nconst DUMMY_URL_BASE = 'http://ternsecure-dummy';\n\nexport type constructUrlWithRedirectProps = {\n signInUrl: string;\n signInPathParam?: string;\n currentPath: string;\n signUpUrl?: string;\n signUpPathParam?: string;\n};\n\ninterface BuildURLParams extends Partial<URL> {\n base?: string;\n hashPath?: string;\n hashSearch?: string;\n hashSearchParams?:\n | URLSearchParams\n | Record<string, string>\n | Array<URLSearchParams | Record<string, string>>;\n}\n\ninterface BuildURLOptions<T> {\n skipOrigin?: boolean;\n stringify?: T;\n}\n\n/**\n *\n * buildURL(params: URLParams, options: BuildURLOptions): string\n *\n * Builds a URL safely by using the native URL() constructor. It can\n * also build a secondary path and search URL that lives inside the hash\n * of the main URL. For example:\n *\n * https://foo.com/bar?qux=42#/hash-bar?hash-qux=42\n *\n * References:\n * https://developer.mozilla.org/en-US/docs/Web/API/URL\n *\n * @param {BuildURLParams} params\n * @param {BuildURLOptions} options\n * @returns {URL | string} Returns the URL href\n */\nexport function buildURL<B extends boolean>(\n params: BuildURLParams,\n options?: BuildURLOptions<B>,\n): B extends true ? string : URL;\n\n/**\n * Builds a URL from given parameters, handling search and hash parameters\n * @param params - The parameters to construct the URL\n * @param options - Options for building the URL\n * @returns The constructed URL as a string or URL object\n */\nexport function buildURL(\n params: BuildURLParams,\n options: BuildURLOptions<boolean> = {},\n): URL | string {\n const { base, hashPath, hashSearch, searchParams, hashSearchParams, ...rest } = params;\n const { stringify, skipOrigin } = options;\n\n let baseFallback = '';\n if (typeof window !== 'undefined' && !!window.location) {\n baseFallback = window.location.href;\n } else {\n baseFallback = 'http://react-native-fake-base-url';\n }\n\n const url = new URL(base || '', baseFallback);\n\n // Handle search parameters\n // params.searchParams comes from Partial<URL>, so it's URLSearchParams | undefined\n if (searchParams instanceof URLSearchParams) {\n searchParams.forEach((value, key) => {\n if (value !== null && value !== undefined) {\n url.searchParams.set(key, value); //camelToSnake(key), value\n }\n });\n }\n\n Object.assign(url, rest);\n\n // Handle hash-related parameters\n if (hashPath || hashSearch || hashSearchParams) {\n const dummyUrlForHash = new URL(DUMMY_URL_BASE + url.hash.substring(1));\n\n dummyUrlForHash.pathname = joinPaths(dummyUrlForHash.pathname, hashPath || '');\n\n const searchParamsFromHashSearchString = getQueryParams(hashSearch || '');\n\n for (const [key, val] of Object.entries(searchParamsFromHashSearchString)) {\n dummyUrlForHash.searchParams.append(key, val);\n }\n const finalHashPath = hashPath || '';\n const queryForHash = new URLSearchParams(hashSearch || '');\n\n if (hashSearchParams) {\n const paramsArr = Array.isArray(hashSearchParams) ? hashSearchParams : [hashSearchParams];\n for (const _params of paramsArr) {\n if (!(_params instanceof URLSearchParams) && typeof _params !== 'object') {\n continue;\n }\n const params = new URLSearchParams(_params);\n params.forEach((value, key) => {\n if (value !== null && value !== undefined) {\n dummyUrlForHash.searchParams.set(camelToSnake(key), value);\n }\n });\n }\n }\n\n const newHash = dummyUrlForHash.href.replace(DUMMY_URL_BASE, '');\n if (newHash !== '/') {\n // Assign them to the hash of the main url\n url.hash = newHash;\n }\n }\n\n if (stringify) {\n return skipOrigin ? url.href.replace(url.origin, '') : url.href;\n }\n return url;\n}\n\n/**\n * Constructs a full URL with the current origin\n * @param path - The path to construct the URL for\n * @returns The full URL with origin\n */\nexport const constructFullUrl = (path: string) => {\n if (typeof window === 'undefined') return path;\n const baseUrl = window.location.origin;\n if (path.startsWith('http')) {\n return path;\n }\n return `${baseUrl}${path.startsWith('/') ? path : `/${path}`}`;\n};\n\n/**\n * Checks if the current URL has a redirect loop\n * @param currentPath - The current pathname\n * @param redirectPath - The path we're trying to redirect to\n * @returns boolean indicating if there's a redirect loop\n */\nexport const hasRedirectLoop = (currentPath: string, redirectPath: string): boolean => {\n if (!currentPath || !redirectPath) return false;\n\n // Remove any query parameters for comparison\n const cleanCurrentPath = currentPath.split('?')[0];\n const cleanRedirectPath = redirectPath.split('?')[0];\n\n return cleanCurrentPath === cleanRedirectPath;\n};\n\nexport const urlWithRedirect = (options: constructUrlWithRedirectProps): string => {\n const {\n signInUrl,\n signInPathParam = '/sign-in',\n currentPath,\n signUpUrl,\n signUpPathParam = '/sign-up',\n } = options;\n\n const baseUrl = window.location.origin;\n\n if (typeof window === 'undefined') {\n return signInUrl;\n }\n\n const url = new URL(signInUrl, baseUrl);\n\n if (!currentPath.includes(signInPathParam) && !currentPath.includes(signUpPathParam)) {\n url.searchParams.set('redirect', currentPath);\n }\n\n return url.toString();\n};\n\n/**\n * Stores the current path before signing out\n */\nexport const storePreviousPath = (path: string): void => {\n if (typeof window !== 'undefined') {\n sessionStorage.setItem('previousPath', path);\n }\n};\n\n/**\n * Gets the stored previous path\n */\nexport const getPreviousPath = (): string | null => {\n if (typeof window !== 'undefined') {\n return sessionStorage.getItem('previousPath');\n }\n return null;\n};\n\n/**\n * Gets a validated redirect URL ensuring it's from the same origin\n * @param redirectUrl - The URL to validate\n * @param searchParams - The search parameters to check for redirect\n * @returns A validated redirect URL\n */\nexport const getValidRedirectUrl = (\n searchParams: URLSearchParams,\n configuredRedirect?: string,\n): string => {\n // Check URL search param first (highest priority)\n const urlRedirect = searchParams.get('redirect');\n if (urlRedirect) {\n return validateUrl(urlRedirect);\n }\n\n // Then check configured redirect (for first visits)\n if (configuredRedirect) {\n return validateUrl(configuredRedirect);\n }\n\n // Default fallback\n return '/';\n};\n\n/**\n * Validates and sanitizes URLs\n */\nconst validateUrl = (url: string): string => {\n try {\n // For absolute URLs\n if (url.startsWith('http')) {\n const urlObj = new URL(url);\n if (typeof window !== 'undefined' && urlObj.origin !== window.location.origin) {\n return '/';\n }\n }\n\n // For relative URLs\n return '/';\n } catch {\n return '/';\n }\n};\n\nexport function toURL(url: string | URL): URL {\n return new URL(url.toString(), window.location.origin);\n}\n"],"mappings":"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","names":["params"]}
1
+ {"version":3,"sources":["../../../src/utils/construct.ts"],"sourcesContent":["import { camelToSnake } from '@tern-secure/shared/caseUtils';\nimport { globs } from '@tern-secure/shared/globs';\nimport { logger } from '@tern-secure/shared/logger';\n\nimport { joinPaths } from './path';\nimport { getQueryParams } from './querystring';\n\nconst DUMMY_URL_BASE = 'http://ternsecure-dummy';\n\nconst BANNED_URI_PROTOCOLS = ['javascript:'] as const;\n\nexport type constructUrlWithRedirectProps = {\n signInUrl: string;\n signInPathParam?: string;\n currentPath: string;\n signUpUrl?: string;\n signUpPathParam?: string;\n};\n\ninterface BuildURLParams extends Partial<URL> {\n base?: string;\n hashPath?: string;\n hashSearch?: string;\n hashSearchParams?:\n | URLSearchParams\n | Record<string, string>\n | Array<URLSearchParams | Record<string, string>>;\n}\n\ninterface BuildURLOptions<T> {\n skipOrigin?: boolean;\n stringify?: T;\n}\n\n/**\n *\n * buildURL(params: URLParams, options: BuildURLOptions): string\n *\n * Builds a URL safely by using the native URL() constructor. It can\n * also build a secondary path and search URL that lives inside the hash\n * of the main URL. For example:\n *\n * https://foo.com/bar?qux=42#/hash-bar?hash-qux=42\n *\n * References:\n * https://developer.mozilla.org/en-US/docs/Web/API/URL\n *\n * @param {BuildURLParams} params\n * @param {BuildURLOptions} options\n * @returns {URL | string} Returns the URL href\n */\nexport function buildURL<B extends boolean>(\n params: BuildURLParams,\n options?: BuildURLOptions<B>,\n): B extends true ? string : URL;\n\nexport function buildURL(\n params: BuildURLParams,\n options: BuildURLOptions<boolean> = {},\n): URL | string {\n const { base, hashPath, hashSearch, searchParams, hashSearchParams, ...rest } = params;\n\n let baseFallback = '';\n if (typeof window !== 'undefined' && !!window.location) {\n baseFallback = window.location.href;\n } else {\n baseFallback = 'http://react-native-fake-base-url';\n }\n\n const url = new URL(base || '', baseFallback);\n\n // Handle search parameters\n // params.searchParams comes from Partial<URL>, so it's URLSearchParams | undefined\n if (searchParams instanceof URLSearchParams) {\n searchParams.forEach((value, key) => {\n if (value !== null && value !== undefined) {\n url.searchParams.set(camelToSnake(key), value);\n }\n });\n }\n\n Object.assign(url, rest);\n\n // Handle hash-related parameters\n if (hashPath || hashSearch || hashSearchParams) {\n const dummyUrlForHash = new URL(DUMMY_URL_BASE + url.hash.substring(1));\n\n dummyUrlForHash.pathname = joinPaths(dummyUrlForHash.pathname, hashPath || '');\n\n const searchParamsFromHashSearchString = getQueryParams(hashSearch || '');\n\n for (const [key, val] of Object.entries(searchParamsFromHashSearchString)) {\n dummyUrlForHash.searchParams.append(key, val);\n }\n\n if (hashSearchParams) {\n const paramsArr = Array.isArray(hashSearchParams) ? hashSearchParams : [hashSearchParams];\n for (const _params of paramsArr) {\n if (!(_params instanceof URLSearchParams) && typeof _params !== 'object') {\n continue;\n }\n const params = new URLSearchParams(_params);\n params.forEach((value, key) => {\n if (value !== null && value !== undefined) {\n dummyUrlForHash.searchParams.set(camelToSnake(key), value);\n }\n });\n }\n }\n\n const newHash = dummyUrlForHash.href.replace(DUMMY_URL_BASE, '');\n if (newHash !== '/') {\n // Assign them to the hash of the main url\n url.hash = newHash;\n }\n }\n\n const { stringify, skipOrigin } = options;\n if (stringify) {\n return skipOrigin ? url.href.replace(url.origin, '') : url.href;\n }\n return url;\n}\n\n/**\n * Constructs a full URL with the current origin\n * @param path - The path to construct the URL for\n * @returns The full URL with origin\n */\nexport const constructFullUrl = (path: string) => {\n if (typeof window === 'undefined') return path;\n const baseUrl = window.location.origin;\n if (path.startsWith('http')) {\n return path;\n }\n return `${baseUrl}${path.startsWith('/') ? path : `/${path}`}`;\n};\n\n/**\n * Checks if the current URL has a redirect loop\n * @param currentPath - The current pathname\n * @param redirectPath - The path we're trying to redirect to\n * @returns boolean indicating if there's a redirect loop\n */\nexport const hasRedirectLoop = (currentPath: string, redirectPath: string): boolean => {\n if (!currentPath || !redirectPath) return false;\n\n // Remove any query parameters for comparison\n const cleanCurrentPath = currentPath.split('?')[0];\n const cleanRedirectPath = redirectPath.split('?')[0];\n\n return cleanCurrentPath === cleanRedirectPath;\n};\n\nexport const urlWithRedirect = (options: constructUrlWithRedirectProps): string => {\n const {\n signInUrl,\n signInPathParam = '/sign-in',\n currentPath,\n signUpUrl,\n signUpPathParam = '/sign-up',\n } = options;\n\n const baseUrl = window.location.origin;\n\n if (typeof window === 'undefined') {\n return signInUrl;\n }\n\n const url = new URL(signInUrl, baseUrl);\n\n if (!currentPath.includes(signInPathParam) && !currentPath.includes(signUpPathParam)) {\n url.searchParams.set('redirect', currentPath);\n }\n\n return url.toString();\n};\n\n/**\n * Stores the current path before signing out\n */\nexport const storePreviousPath = (path: string): void => {\n if (typeof window !== 'undefined') {\n sessionStorage.setItem('previousPath', path);\n }\n};\n\n/**\n * Gets the stored previous path\n */\nexport const getPreviousPath = (): string | null => {\n if (typeof window !== 'undefined') {\n return sessionStorage.getItem('previousPath');\n }\n return null;\n};\n\n/**\n * Gets a validated redirect URL ensuring it's from the same origin\n * @param redirectUrl - The URL to validate\n * @param searchParams - The search parameters to check for redirect\n * @returns A validated redirect URL\n */\nexport const getValidRedirectUrl = (\n searchParams: URLSearchParams,\n configuredRedirect?: string,\n): string => {\n // Check URL search param first (highest priority)\n const urlRedirect = searchParams.get('redirect');\n if (urlRedirect) {\n return validateUrl(urlRedirect);\n }\n\n // Then check configured redirect (for first visits)\n if (configuredRedirect) {\n return validateUrl(configuredRedirect);\n }\n\n // Default fallback\n return '/';\n};\n\n/**\n * Validates and sanitizes URLs\n */\nconst validateUrl = (url: string): string => {\n try {\n // For absolute URLs\n if (url.startsWith('http')) {\n const urlObj = new URL(url);\n if (typeof window !== 'undefined' && urlObj.origin !== window.location.origin) {\n return '/';\n }\n }\n\n // For relative URLs\n return '/';\n } catch {\n return '/';\n }\n};\n\nexport function toURL(url: string | URL): URL {\n return new URL(url.toString(), window.location.origin);\n}\n\n/**\n *\n * stripOrigin(url: URL | string): string\n *\n * Strips the origin part of a URL and preserves path, search and hash is applicable\n *\n * References:\n * https://developer.mozilla.org/en-US/docs/Web/API/URL\n *\n * @param {URL | string} url\n * @returns {string} Returns the URL href without the origin\n */\nexport function stripOrigin(url: URL | string): string {\n url = toURL(url);\n return url.href.replace(url.origin, '');\n}\n\n/**\n * trimTrailingSlash(path: string): string\n *\n * Strips the trailing slashes from a string\n *\n * @returns {string} Returns the string without trailing slashes\n * @param path\n */\nexport const trimTrailingSlash = (path: string): string => {\n return (path || '').replace(/\\/+$/, '');\n};\n\nexport function isValidUrl(val: unknown): val is string {\n if (!val) {\n return false;\n }\n\n try {\n new URL(val as string);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function relativeToAbsoluteUrl(url: string, origin: string | URL): URL {\n try {\n return new URL(url);\n } catch {\n return new URL(url, origin);\n }\n}\n\n// Regular expression to detect disallowed patterns\nconst disallowedPatterns = [\n /\\0/, // Null bytes\n /^\\/\\//, // Protocol-relative\n // eslint-disable-next-line no-control-regex\n /[\\x00-\\x1F]/, // Control characters\n];\n\n/**\n * Check for potentially problematic URLs that could have been crafted to intentionally bypass the origin check. Note that the URLs passed to this\n * function are assumed to be from an \"allowed origin\", so we are not executing origin-specific checks here.\n */\nexport function isProblematicUrl(url: URL): boolean {\n if (hasBannedProtocol(url)) {\n return true;\n }\n // Check against disallowed patterns\n for (const pattern of disallowedPatterns) {\n if (pattern.test(url.pathname)) {\n return true;\n }\n }\n\n return false;\n}\n\nexport function hasBannedProtocol(val: string | URL) {\n if (!isValidUrl(val)) {\n return false;\n }\n const protocol = new URL(val).protocol;\n return BANNED_URI_PROTOCOLS.some(bp => bp === protocol);\n}\n\nexport const isAllowedRedirect =\n (allowedRedirectOrigins: Array<string | RegExp> | undefined, currentOrigin: string) =>\n (_url: URL | string) => {\n // On server-side (no origin), allow all redirects\n // They will be validated on client-side\n if (!currentOrigin) return true;\n\n let url = _url;\n if (typeof url === 'string') {\n url = relativeToAbsoluteUrl(url, currentOrigin);\n }\n\n if (!allowedRedirectOrigins) {\n return true;\n }\n\n const isSameOrigin = currentOrigin === url.origin;\n\n const isAllowed =\n !isProblematicUrl(url) &&\n (isSameOrigin ||\n allowedRedirectOrigins\n .map(origin =>\n typeof origin === 'string' ? globs.toRegexp(trimTrailingSlash(origin)) : origin,\n )\n .some(origin => origin.test(trimTrailingSlash(url.origin))));\n\n if (!isAllowed) {\n logger.warnOnce(\n `Clerk: Redirect URL ${url} is not on one of the allowedRedirectOrigins, falling back to the default redirect URL.`,\n );\n }\n return isAllowed;\n };\n"],"mappings":"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","names":["params"]}
@@ -17,9 +17,13 @@ var utils_exports = {};
17
17
  module.exports = __toCommonJS(utils_exports);
18
18
  __reExport(utils_exports, require("./construct"), module.exports);
19
19
  __reExport(utils_exports, require("./querystring"), module.exports);
20
+ __reExport(utils_exports, require("./redirectUrls"), module.exports);
21
+ __reExport(utils_exports, require("./windowNavigate"), module.exports);
20
22
  // Annotate the CommonJS export names for ESM import in node:
21
23
  0 && (module.exports = {
22
24
  ...require("./construct"),
23
- ...require("./querystring")
25
+ ...require("./querystring"),
26
+ ...require("./redirectUrls"),
27
+ ...require("./windowNavigate")
24
28
  });
25
29
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/index.ts"],"sourcesContent":["export * from './construct';\nexport * from './querystring';\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA;AAAA,0BAAc,wBAAd;AACA,0BAAc,0BADd;","names":[]}
1
+ {"version":3,"sources":["../../../src/utils/index.ts"],"sourcesContent":["export * from './construct';\nexport * from './querystring';\nexport * from './redirectUrls';\nexport * from './windowNavigate';\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA;AAAA,0BAAc,wBAAd;AACA,0BAAc,0BADd;AAEA,0BAAc,2BAFd;AAGA,0BAAc,6BAHd;","names":[]}
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var redirectUrls_exports = {};
20
+ __export(redirectUrls_exports, {
21
+ RedirectUrls: () => RedirectUrls
22
+ });
23
+ module.exports = __toCommonJS(redirectUrls_exports);
24
+ var import_caseUtils = require("@tern-secure/shared/caseUtils");
25
+ var import_object = require("@tern-secure/shared/object");
26
+ var import_construct = require("./construct");
27
+ class RedirectUrls {
28
+ static keys = [
29
+ "signInForceRedirectUrl",
30
+ "signInFallbackRedirectUrl",
31
+ "signUpForceRedirectUrl",
32
+ "signUpFallbackRedirectUrl",
33
+ "afterSignInUrl",
34
+ "afterSignUpUrl",
35
+ "redirectUrl"
36
+ ];
37
+ static preserved = ["redirectUrl"];
38
+ options;
39
+ fromOptions;
40
+ fromProps;
41
+ fromSearchParams;
42
+ constructor(options, props = {}, searchParams = {}) {
43
+ this.options = options;
44
+ this.fromOptions = this.#parse(options || {});
45
+ this.fromProps = this.#parse(props || {});
46
+ this.fromSearchParams = this.#parseSearchParams(searchParams || {});
47
+ }
48
+ getAfterSignInUrl() {
49
+ return this.#getRedirectUrl("signIn");
50
+ }
51
+ getAfterSignUpUrl() {
52
+ return this.#getRedirectUrl("signUp");
53
+ }
54
+ getPreservedSearchParams() {
55
+ return this.#toSearchParams(this.#flattenPreserved());
56
+ }
57
+ toSearchParams() {
58
+ return this.#toSearchParams(this.#flattenAll());
59
+ }
60
+ #toSearchParams(obj) {
61
+ const camelCased = Object.fromEntries(
62
+ Object.entries(obj).map(([key, value]) => [(0, import_caseUtils.camelToSnake)(key), value])
63
+ );
64
+ return new URLSearchParams((0, import_object.removeUndefined)(camelCased));
65
+ }
66
+ #flattenPreserved() {
67
+ return Object.fromEntries(
68
+ Object.entries({ ...this.fromSearchParams }).filter(
69
+ ([key]) => RedirectUrls.preserved.includes(key)
70
+ )
71
+ );
72
+ }
73
+ #flattenAll() {
74
+ const signUpForceRedirectUrl = this.fromSearchParams.signUpForceRedirectUrl || this.fromProps.signUpForceRedirectUrl || this.fromOptions.signUpForceRedirectUrl;
75
+ const signUpFallbackRedirectUrl = this.fromSearchParams.signUpFallbackRedirectUrl || this.fromProps.signUpFallbackRedirectUrl || this.fromOptions.signUpFallbackRedirectUrl;
76
+ const signInForceRedirectUrl = this.fromSearchParams.signInForceRedirectUrl || this.fromProps.signInForceRedirectUrl || this.fromOptions.signInForceRedirectUrl;
77
+ const signInFallbackRedirectUrl = this.fromSearchParams.signInFallbackRedirectUrl || this.fromProps.signInFallbackRedirectUrl || this.fromOptions.signInFallbackRedirectUrl;
78
+ const afterSignInUrl = this.fromSearchParams.afterSignInUrl || this.fromProps.afterSignInUrl || this.fromOptions.afterSignInUrl;
79
+ const afterSignUpUrl = this.fromSearchParams.afterSignUpUrl || this.fromProps.afterSignUpUrl || this.fromOptions.afterSignUpUrl;
80
+ const redirectUrl = this.fromSearchParams.redirectUrl || this.fromProps.redirectUrl || this.fromOptions.redirectUrl;
81
+ const res = {
82
+ signUpForceRedirectUrl,
83
+ signUpFallbackRedirectUrl,
84
+ signInForceRedirectUrl,
85
+ signInFallbackRedirectUrl,
86
+ afterSignInUrl,
87
+ afterSignUpUrl,
88
+ redirectUrl
89
+ };
90
+ return res;
91
+ }
92
+ #getRedirectUrl(prefix) {
93
+ const forceKey = `${prefix}ForceRedirectUrl`;
94
+ const fallbackKey = `${prefix}FallbackRedirectUrl`;
95
+ let newKeyInUse;
96
+ let result;
97
+ result = this.fromSearchParams[forceKey] || this.fromProps[forceKey] || this.fromOptions[forceKey];
98
+ if (result) {
99
+ newKeyInUse = forceKey;
100
+ }
101
+ result ||= this.fromSearchParams.redirectUrl;
102
+ if (result) {
103
+ newKeyInUse = "redirectUrl";
104
+ }
105
+ result ||= this.fromSearchParams[fallbackKey] || this.fromProps[fallbackKey] || this.fromOptions[fallbackKey];
106
+ if (result) {
107
+ newKeyInUse = fallbackKey;
108
+ }
109
+ if (!result) {
110
+ if (typeof window === "undefined") {
111
+ return "/";
112
+ }
113
+ return window.location.href;
114
+ }
115
+ return result || "/";
116
+ }
117
+ #parse(obj) {
118
+ const res = {};
119
+ RedirectUrls.keys.forEach((key) => {
120
+ res[key] = obj[key];
121
+ });
122
+ return (0, import_object.applyFunctionToObj)(
123
+ this.#filterRedirects(this.#toAbsoluteUrls((0, import_object.filterProps)(res, Boolean))),
124
+ (val) => val.toString()
125
+ );
126
+ }
127
+ #parseSearchParams(obj) {
128
+ const res = {};
129
+ RedirectUrls.keys.forEach((key) => {
130
+ if (obj instanceof URLSearchParams) {
131
+ res[key] = obj.get((0, import_caseUtils.camelToSnake)(key));
132
+ } else {
133
+ res[key] = obj[(0, import_caseUtils.camelToSnake)(key)];
134
+ }
135
+ });
136
+ return (0, import_object.applyFunctionToObj)(
137
+ this.#filterRedirects(this.#toAbsoluteUrls((0, import_object.filterProps)(res, Boolean))),
138
+ (val) => val.toString()
139
+ );
140
+ }
141
+ #toAbsoluteUrls(obj) {
142
+ const origin = typeof window !== "undefined" ? window.location.origin : "";
143
+ if (!origin) return obj;
144
+ return (0, import_object.applyFunctionToObj)(obj, (url) => (0, import_construct.relativeToAbsoluteUrl)(url, origin));
145
+ }
146
+ #filterRedirects = (obj) => {
147
+ var _a;
148
+ const origin = typeof window !== "undefined" ? window.location.origin : "";
149
+ return (0, import_object.filterProps)(obj, (0, import_construct.isAllowedRedirect)((_a = this.options) == null ? void 0 : _a.allowedRedirectOrigins, origin));
150
+ };
151
+ }
152
+ // Annotate the CommonJS export names for ESM import in node:
153
+ 0 && (module.exports = {
154
+ RedirectUrls
155
+ });
156
+ //# sourceMappingURL=redirectUrls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/redirectUrls.ts"],"sourcesContent":["import { camelToSnake } from '@tern-secure/shared/caseUtils';\nimport { applyFunctionToObj, filterProps, removeUndefined } from '@tern-secure/shared/object';\nimport type { RedirectOptions, TernSecureAuthOptions } from '@tern-secure/types';\n\nimport { isAllowedRedirect, relativeToAbsoluteUrl } from './construct';\n\n/**\n * RedirectUrls class handles all redirect URL construction logic\n * for sign-in, sign-up, and post-authentication flows.\n *\n * This class centralizes the redirect logic previously scattered across\n * multiple methods, making it reusable and maintainable.\n */\nexport class RedirectUrls {\n private static keys: (keyof RedirectOptions)[] = [\n 'signInForceRedirectUrl',\n 'signInFallbackRedirectUrl',\n 'signUpForceRedirectUrl',\n 'signUpFallbackRedirectUrl',\n 'afterSignInUrl',\n 'afterSignUpUrl',\n 'redirectUrl',\n ];\n\n private static preserved = ['redirectUrl'];\n\n private readonly options: TernSecureAuthOptions;\n private readonly fromOptions: RedirectOptions;\n private readonly fromProps: RedirectOptions;\n private readonly fromSearchParams: RedirectOptions & { redirectUrl?: string | null };\n\n constructor(options: TernSecureAuthOptions, props: RedirectOptions = {}, searchParams: any = {}) {\n this.options = options;\n this.fromOptions = this.#parse(options || {});\n this.fromProps = this.#parse(props || {});\n this.fromSearchParams = this.#parseSearchParams(searchParams || {});\n }\n\n getAfterSignInUrl() {\n return this.#getRedirectUrl('signIn');\n }\n\n getAfterSignUpUrl() {\n return this.#getRedirectUrl('signUp');\n }\n\n getPreservedSearchParams() {\n return this.#toSearchParams(this.#flattenPreserved());\n }\n\n toSearchParams() {\n return this.#toSearchParams(this.#flattenAll());\n }\n\n #toSearchParams(obj: Record<string, string | undefined | null>): URLSearchParams {\n const camelCased = Object.fromEntries(\n Object.entries(obj).map(([key, value]) => [camelToSnake(key), value]),\n );\n return new URLSearchParams(removeUndefined(camelCased) as Record<string, string>);\n }\n\n #flattenPreserved() {\n return Object.fromEntries(\n Object.entries({ ...this.fromSearchParams }).filter(([key]) =>\n RedirectUrls.preserved.includes(key),\n ),\n );\n }\n\n #flattenAll() {\n const signUpForceRedirectUrl =\n this.fromSearchParams.signUpForceRedirectUrl ||\n this.fromProps.signUpForceRedirectUrl ||\n this.fromOptions.signUpForceRedirectUrl;\n const signUpFallbackRedirectUrl =\n this.fromSearchParams.signUpFallbackRedirectUrl ||\n this.fromProps.signUpFallbackRedirectUrl ||\n this.fromOptions.signUpFallbackRedirectUrl;\n const signInForceRedirectUrl =\n this.fromSearchParams.signInForceRedirectUrl ||\n this.fromProps.signInForceRedirectUrl ||\n this.fromOptions.signInForceRedirectUrl;\n const signInFallbackRedirectUrl =\n this.fromSearchParams.signInFallbackRedirectUrl ||\n this.fromProps.signInFallbackRedirectUrl ||\n this.fromOptions.signInFallbackRedirectUrl;\n const afterSignInUrl =\n this.fromSearchParams.afterSignInUrl ||\n this.fromProps.afterSignInUrl ||\n this.fromOptions.afterSignInUrl;\n const afterSignUpUrl =\n this.fromSearchParams.afterSignUpUrl ||\n this.fromProps.afterSignUpUrl ||\n this.fromOptions.afterSignUpUrl;\n const redirectUrl =\n this.fromSearchParams.redirectUrl ||\n this.fromProps.redirectUrl ||\n this.fromOptions.redirectUrl;\n\n const res: RedirectOptions = {\n signUpForceRedirectUrl,\n signUpFallbackRedirectUrl,\n signInForceRedirectUrl,\n signInFallbackRedirectUrl,\n afterSignInUrl,\n afterSignUpUrl,\n redirectUrl,\n };\n return res;\n }\n\n #getRedirectUrl(prefix: 'signIn' | 'signUp') {\n const forceKey = `${prefix}ForceRedirectUrl` as const;\n const fallbackKey = `${prefix}FallbackRedirectUrl` as const;\n\n let newKeyInUse: string | undefined;\n\n let result;\n // Prioritize forceRedirectUrl\n result =\n this.fromSearchParams[forceKey] || this.fromProps[forceKey] || this.fromOptions[forceKey];\n if (result) {\n newKeyInUse = forceKey;\n }\n\n // Try to get redirect_url, only allowed as a search param\n result ||= this.fromSearchParams.redirectUrl;\n if (result) {\n newKeyInUse = 'redirectUrl';\n }\n\n // Otherwise, fallback to fallbackRedirectUrl\n result ||=\n this.fromSearchParams[fallbackKey] ||\n this.fromProps[fallbackKey] ||\n this.fromOptions[fallbackKey];\n if (result) {\n newKeyInUse = fallbackKey;\n }\n\n if (!result) {\n if (typeof window === 'undefined') {\n return '/';\n }\n return window.location.href;\n }\n return result || '/';\n }\n\n #parse(obj: unknown) {\n const res = {} as RedirectOptions;\n RedirectUrls.keys.forEach(key => {\n // @ts-expect-error\n res[key] = obj[key];\n });\n\n //const absoluteUrls = this.#toAbsoluteUrls(filterProps(res, Boolean));\n //const filtered = this.#filterRedirects(absoluteUrls);\n //return applyFunctionToObj(filtered, val => val.toString());\n\n return applyFunctionToObj(\n this.#filterRedirects(this.#toAbsoluteUrls(filterProps(res, Boolean))),\n val => val.toString(),\n );\n }\n\n #parseSearchParams(obj: any) {\n const res = {} as typeof this.fromSearchParams;\n RedirectUrls.keys.forEach(key => {\n if (obj instanceof URLSearchParams) {\n res[key] = obj.get(camelToSnake(key));\n } else {\n res[key] = obj[camelToSnake(key)];\n }\n });\n\n return applyFunctionToObj(\n this.#filterRedirects(this.#toAbsoluteUrls(filterProps(res, Boolean))),\n val => val.toString(),\n );\n }\n\n #toAbsoluteUrls(obj: RedirectOptions) {\n const origin = typeof window !== 'undefined' ? window.location.origin : '';\n // If no origin (server-side), return URLs as-is without conversion\n // They will be properly converted on the client-side\n if (!origin) return obj;\n\n return applyFunctionToObj(obj, (url: string) => relativeToAbsoluteUrl(url, origin));\n }\n\n #filterRedirects = (obj: RedirectOptions) => {\n const origin = typeof window !== 'undefined' ? window.location.origin : '';\n return filterProps(obj, isAllowedRedirect(this.options?.allowedRedirectOrigins, origin));\n };\n}\n"],"mappings":"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","names":[]}