-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat(#2): connection with Spotify * feat(backend): creation of account if not already & generate types * refactor: formatting auth routing * feat: auth automatism * chore: some typo & added share Database type with monorepo * chore: logical to force new user to choice username * fix: spam database to get user-profile * chore: improve some log message & UX with error handler * fix: auth with Spotify provider in mobile * fix: misstake with expo Screen * feat(backend): added route /auth/redirection * chore: improved error response * chore: added other method to store supabase on client * refactor(backend): improve lisibility of auth/callback * chore(expo): refacto Alert & added scope of Spotify provider * chore(backend): reponse to pull request * chore(expo): added env variable to expo project with skeleton * refactor(expo): improve code comprehension * chore: removed unnecessary code
- Loading branch information
1 parent
0baa50f
commit 7b655ba
Showing
27 changed files
with
1,458 additions
and
36 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { createServerClient } from "@supabase/ssr"; | ||
import { FastifyReply, FastifyRequest } from "fastify"; | ||
|
||
export default function createClient(context: { | ||
request: FastifyRequest; | ||
response: FastifyReply; | ||
}) { | ||
if (!process.env.SUPABASE_URL || !process.env.SUPABASE_ANON_KEY) { | ||
throw new Error( | ||
"Missing SUPABASE_URL or SUPABASE_ANON_KEY environment variable" | ||
); | ||
} | ||
return createServerClient( | ||
process.env.SUPABASE_URL, | ||
process.env.SUPABASE_ANON_KEY, | ||
{ | ||
cookies: { | ||
get: (key: any) => { | ||
const cookies = context.request.cookies; | ||
const cookie = cookies[key] ?? ""; | ||
return decodeURIComponent(cookie); | ||
}, | ||
set: (key: any, value: any, options: any) => { | ||
if (!context.response) return; | ||
context.response.cookie(key, encodeURIComponent(value), { | ||
...options, | ||
sameSite: "Lax", | ||
httpOnly: true, | ||
}); | ||
}, | ||
remove: (key: any, options: any) => { | ||
if (!context.response) return; | ||
context.response.cookie(key, "", { ...options, httpOnly: true }); | ||
}, | ||
}, | ||
} | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import { FastifyRequest, FastifyReply } from "fastify"; | ||
import createClient from "../lib/supabase"; | ||
import { adminSupabase } from "../server"; | ||
import { Database } from "../types/dbTypes"; | ||
import { PostgrestError } from "@supabase/supabase-js"; | ||
|
||
enum StreamingService { | ||
Spotify = "a2d17b25-d87e-42af-9e79-fd4df6b59222", | ||
SoundCloud = "c99631a2-f06c-4076-80c2-13428944c3a8", | ||
} | ||
|
||
export default async function AuthCallbackGET( | ||
request: FastifyRequest, | ||
response: FastifyReply | ||
) { | ||
const supabaseUrl = process.env.SUPABASE_URL; | ||
if (!supabaseUrl) | ||
return response.code(400).send({ error: "Missing SUPABASE_URL" }); | ||
const supabaseId = supabaseUrl.split(".")[0].split("//")[1]; | ||
|
||
if (!request.cookies["sb-" + supabaseId + "-auth-token-code-verifier"]) { | ||
return response | ||
.code(400) | ||
.send({ error: "Missing cookie auth-token-code-verifier " }); | ||
} | ||
|
||
const supabase = createClient({ | ||
request, | ||
response, | ||
}); | ||
const code = ( | ||
request.query as { | ||
code: string; | ||
} | ||
).code; | ||
if (!code) response.code(400).send({ error: "Missing code" }); | ||
|
||
const { data } = await supabase.auth.exchangeCodeForSession(code); | ||
if (!data.session) | ||
return response.code(400).send({ error: "Missing session" }); | ||
const providerToken = data.session.provider_token; | ||
const providerRefreshToken = data.session.provider_refresh_token; | ||
const providerName = data.session.user.app_metadata.provider; | ||
|
||
if (!providerToken || !providerRefreshToken) | ||
return response | ||
.code(400) | ||
.send({ error: "Missing provider token from " + providerName }); | ||
|
||
// verify if user already have an user_profile (if new acc, create one) | ||
let userProfileId = await getUserProfile(data.user.id); | ||
|
||
if (!userProfileId) { | ||
// New account | ||
const { userProfileId: newUserProfileId, error } = await createAccount({ | ||
full_name: data.user.user_metadata.full_name, | ||
account_id: data.user.id, | ||
username: null, | ||
}); | ||
if (error || !newUserProfileId) { | ||
request.log.error("Impossible to create account: " + error); | ||
return response.code(500).send({ error: error }); | ||
} | ||
userProfileId = newUserProfileId; | ||
} | ||
const providerTokenEnd = new Date(); | ||
providerTokenEnd.setHours(providerTokenEnd.getHours() + 1); | ||
const timestampZProviderTokenEnd = providerTokenEnd.toISOString(); | ||
|
||
const error = await upsertService({ | ||
access_token: providerToken, | ||
refresh_token: providerRefreshToken, | ||
expires_in: timestampZProviderTokenEnd, | ||
user_profile_id: userProfileId, | ||
service_id: StreamingService.Spotify, | ||
}); | ||
|
||
if (error) { | ||
request.log.error("Upsert impossible, ", error); | ||
return response.code(500).send({ error: "Server error." }); | ||
} | ||
|
||
const refresh_token = data.session.refresh_token; | ||
const redirectUrl = decodeURIComponent(request.url).split("redirect_url=")[1]; | ||
|
||
// redirect user to the redirect url with the refresh token | ||
response.redirect( | ||
redirectUrl + "#refresh_token=" + encodeURIComponent(refresh_token) | ||
); | ||
} | ||
|
||
const getUserProfile = async (userId: string): Promise<string | null> => { | ||
const { data: userData } = await adminSupabase | ||
.from("user_profile") | ||
.select("*") | ||
.eq("account_id", userId) | ||
.single(); | ||
return userData?.user_profile_id ?? null; | ||
}; | ||
|
||
const alreadyBoundService = async ({ | ||
service, | ||
user_profile_id, | ||
}: { | ||
service: StreamingService; | ||
user_profile_id: string; | ||
}): Promise<{ alreadyBound: boolean; error: PostgrestError | null }> => { | ||
const { data, error } = await adminSupabase | ||
.from("bound_services") | ||
.select("*") | ||
.eq("user_profile_id", user_profile_id) | ||
.eq("service_id", service); | ||
return { | ||
alreadyBound: data !== null && data.length > 0, | ||
error: error, | ||
}; | ||
}; | ||
|
||
const createAccount = async ({ | ||
full_name, | ||
account_id, | ||
username, | ||
}: { | ||
full_name: string; | ||
account_id: string; | ||
username: string | null; | ||
}): Promise<{ userProfileId: string | null; error: PostgrestError | null }> => { | ||
const { data, error } = await adminSupabase | ||
.from("profile") | ||
.insert({ | ||
nickname: full_name, | ||
}) | ||
.select("*") | ||
.single(); | ||
|
||
const user_profile_id = data?.id; | ||
|
||
if (!user_profile_id) | ||
return { | ||
userProfileId: null, | ||
error: error, | ||
}; | ||
|
||
const { data: dataUserProfile, error: errorUserprofile } = await adminSupabase | ||
.from("user_profile") | ||
.insert({ | ||
account_id: account_id, | ||
user_profile_id: user_profile_id, | ||
username: username, // Add the missing 'username' property | ||
}); | ||
if (errorUserprofile) | ||
return { | ||
userProfileId: null, | ||
error: errorUserprofile, | ||
}; | ||
return { | ||
userProfileId: user_profile_id, | ||
error: null, | ||
}; | ||
}; | ||
|
||
const upsertService = async ( | ||
service: Database["public"]["Tables"]["bound_services"]["Row"] | ||
): Promise<PostgrestError | null> => { | ||
const { error } = await adminSupabase.from("bound_services").upsert(service); | ||
return error; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { FastifyRequest, FastifyReply } from "fastify"; | ||
|
||
export default function AuthRedirectionGET( | ||
req: FastifyRequest, | ||
reply: FastifyReply | ||
) { | ||
reply.header("Content-Type", "text/html"); | ||
reply.code(200).send(ReponseHTML); | ||
} | ||
|
||
const ReponseHTML = ` | ||
<html> | ||
<script> | ||
const url = window.location.href; | ||
const code_verifier = url.split("#")[1].split("=")[1]; | ||
const redirect_url = url.split("redirect_url=")[1].split("#")[0]; | ||
console.log("url", url); | ||
console.log("code_verifier", code_verifier); | ||
console.log("redirect url", redirect_url); | ||
document.cookie = "sb-ckalsdcwrofxvgxslwiv-auth-token-code-verifier=" + code_verifier + "; path=/"; | ||
// redirect to the redirect url | ||
window.location.href = redirect_url; | ||
</script> | ||
</html>`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
EXPO_PUBLIC_SUPABASE_URL= | ||
EXPO_PUBLIC_SUPABASE_ANON_KEY= | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,5 @@ web-build/ | |
# The following patterns were generated by expo-cli | ||
|
||
expo-env.d.ts | ||
# @end expo-cli | ||
# @end expo-cli | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { Stack } from "expo-router"; | ||
|
||
export default function TabLayout() { | ||
return ( | ||
<Stack> | ||
<Stack.Screen name="index" options={{ headerShown: false }} /> | ||
<Stack.Screen | ||
name="login" | ||
options={{ presentation: "modal", title: "Connexion" }} | ||
/> | ||
<Stack.Screen name="ask-name" options={{ headerShown: false }} /> | ||
</Stack> | ||
); | ||
} |
Oops, something went wrong.