gotrue.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import type { JwtPayload } from '@supabase/supabase-js'
  2. import { type User } from 'common/auth'
  3. import { gotrueClient } from 'common/gotrue'
  4. export const auth = gotrueClient
  5. export const DEFAULT_FALLBACK_PATH = '/organizations'
  6. export const validateReturnTo = (
  7. returnTo: string,
  8. fallback: string = DEFAULT_FALLBACK_PATH
  9. ): string => {
  10. // Block protocol-relative URLs and external URLs
  11. if (returnTo.startsWith('//') || returnTo.includes('://')) {
  12. return fallback
  13. }
  14. // For internal paths:
  15. // 1. Must start with /
  16. // 2. Only allow alphanumeric chars, slashes, hyphens, underscores
  17. // 3. For query params, also allow =, &, and ?
  18. const safePathPattern = /^\/[a-zA-Z0-9/\-_]*(?:\?[a-zA-Z0-9\-_=&]*)?$/
  19. return safePathPattern.test(returnTo) ? returnTo : fallback
  20. }
  21. export const getUserClaims = async (
  22. token: String
  23. ): Promise<{ error: any | null; claims: JwtPayload | null }> => {
  24. try {
  25. const { data, error } = await auth.getClaims(token.replace(/bearer /i, ''))
  26. if (error) throw error
  27. return { claims: data?.claims ?? null, error: null }
  28. } catch (err) {
  29. console.error(err)
  30. return { claims: null, error: err }
  31. }
  32. }
  33. export const getAuth0Id = (provider: String, providerId: String): String => {
  34. return `${provider}|${providerId}`
  35. }
  36. export const getIdentity = (gotrueUser: User) => {
  37. try {
  38. if (gotrueUser !== undefined && gotrueUser.identities !== undefined) {
  39. return { identity: gotrueUser.identities[0], error: null }
  40. }
  41. throw 'Missing identity'
  42. } catch (err) {
  43. return { identity: null, error: err }
  44. }
  45. }
  46. /**
  47. * Transfers the search params from the current location path to a newly built path
  48. */
  49. export const buildPathWithParams = (pathname: string) => {
  50. const [basePath, existingParams] = pathname.split('?', 2)
  51. const pathnameSearchParams = new URLSearchParams(existingParams || '')
  52. // Merge the parameters, with pathname parameters taking precedence
  53. // over the current location's search parameters
  54. const mergedParams = new URLSearchParams(location.search)
  55. for (const [key, value] of pathnameSearchParams.entries()) {
  56. mergedParams.set(key, value)
  57. }
  58. const queryString = mergedParams.toString()
  59. return queryString ? `${basePath}?${queryString}` : basePath
  60. }
  61. export const getReturnToPath = (fallback = DEFAULT_FALLBACK_PATH) => {
  62. // If we're in a server environment, return the fallback
  63. if (typeof location === 'undefined') {
  64. return fallback
  65. }
  66. const searchParams = new URLSearchParams(location.search)
  67. let returnTo = searchParams.get('returnTo') ?? fallback
  68. if (process.env.NEXT_PUBLIC_BASE_PATH) {
  69. returnTo = returnTo.replace(process.env.NEXT_PUBLIC_BASE_PATH, '')
  70. }
  71. searchParams.delete('returnTo')
  72. const remainingSearchParams = searchParams.toString()
  73. const validReturnTo = validateReturnTo(returnTo, fallback)
  74. const [path, existingQuery] = validReturnTo.split('?')
  75. const finalSearchParams = new URLSearchParams(existingQuery || '')
  76. // Add all remaining search params to the final search params
  77. if (remainingSearchParams) {
  78. const remainingParams = new URLSearchParams(remainingSearchParams)
  79. remainingParams.forEach((value, key) => {
  80. finalSearchParams.append(key, value)
  81. })
  82. }
  83. const finalQuery = finalSearchParams.toString()
  84. return path + (finalQuery ? `?${finalQuery}` : '')
  85. }