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