Working User Authorization Flow
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
|
||||
export async function fetchAuthRedirectUri(api_url: string): Promise<{
|
||||
uri: string;
|
||||
callbackKey: string;
|
||||
}> {
|
||||
const client: AxiosInstance = axios.create({
|
||||
baseURL: api_url || "",
|
||||
timeout: 5000,
|
||||
});
|
||||
try {
|
||||
const res = await client.get("/v1/auth/uri");
|
||||
const d = res.data ?? {};
|
||||
const uri = d.data.uri;
|
||||
const callbackKey = d.data.callbackKey;
|
||||
if (typeof uri !== "string" || !uri)
|
||||
throw new Error("redirect uri missing from response");
|
||||
return {
|
||||
uri,
|
||||
callbackKey,
|
||||
};
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to fetch auth redirect uri: ${(e as Error).message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
||||
export * from "./axios";
|
||||
//export * from "./axios";
|
||||
export * from "./user";
|
||||
|
||||
+42
-33
@@ -1,5 +1,7 @@
|
||||
import { getRequestEvent } from "$app/server";
|
||||
import { redirect } from "@sveltejs/kit";
|
||||
import { PUBLIC_API_URL } from "$env/static/public";
|
||||
import { redirect, RequestEvent } from "@sveltejs/kit";
|
||||
import axios from "axios";
|
||||
import { io } from "socket.io-client";
|
||||
|
||||
export const user = {
|
||||
@@ -9,39 +11,51 @@ export const user = {
|
||||
return !!authToken;
|
||||
},
|
||||
|
||||
logout() {
|
||||
const event = getRequestEvent();
|
||||
async refreshSession(refreshToken: string) {
|
||||
const refreshedTokens = (
|
||||
await axios.post(
|
||||
`${PUBLIC_API_URL}/v1/auth/refresh`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
"x-refresh-token": refreshToken,
|
||||
},
|
||||
},
|
||||
)
|
||||
).data.data;
|
||||
|
||||
console.log("Refreshed tokens:", refreshedTokens);
|
||||
return refreshedTokens;
|
||||
},
|
||||
|
||||
logout(event: RequestEvent) {
|
||||
if (!event) return;
|
||||
|
||||
// Clear authentication cookies
|
||||
event.cookies.delete("authToken", { path: "/" });
|
||||
event.cookies.delete("refreshToken", { path: "/" });
|
||||
|
||||
return redirect(303, "/");
|
||||
return redirect(303, "/login");
|
||||
},
|
||||
|
||||
/**
|
||||
* @todo Get communication with server working and setup a key system so that the frontend can listen for a specific key from the backend so that nobody can poach off of login events.
|
||||
*
|
||||
* Note: This function no longer mutates SvelteKit request event/cookies asynchronously.
|
||||
* It returns the tokens to the caller so the caller (within the same request lifecycle)
|
||||
* can set cookies using the event object synchronously.
|
||||
*/
|
||||
async awaitAuthCallback(): Promise<{
|
||||
authToken: string;
|
||||
async awaitAuthCallback(callbackKey: string): Promise<{
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
}> {
|
||||
const event = getRequestEvent();
|
||||
if (!event) return Promise.reject(new Error("No request event"));
|
||||
|
||||
const state =
|
||||
event.url.searchParams.get("state") ??
|
||||
event.cookies.get("authState") ??
|
||||
"";
|
||||
if (!state)
|
||||
return Promise.reject(new Error("Missing state to correlate socket"));
|
||||
|
||||
const base = process.env.API_URL || "";
|
||||
const base = PUBLIC_API_URL || "";
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let settled = false;
|
||||
const socket = io(base, { auth: { state }, transports: ["websocket"] });
|
||||
const socket = io(`${base}/auth_callback`, {
|
||||
transports: ["websocket"],
|
||||
});
|
||||
const timeout = setTimeout(
|
||||
() => {
|
||||
if (settled) return;
|
||||
@@ -56,25 +70,15 @@ export const user = {
|
||||
|
||||
const handlePayload = (payload: any) => {
|
||||
try {
|
||||
const { authToken, refreshToken } = payload ?? {};
|
||||
if (authToken && refreshToken) {
|
||||
const { accessToken, refreshToken } = payload ?? {};
|
||||
if (accessToken && refreshToken) {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
event.cookies.set("authToken", authToken, {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
sameSite: "lax",
|
||||
});
|
||||
event.cookies.set("refreshToken", refreshToken, {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
sameSite: "lax",
|
||||
});
|
||||
clearTimeout(timeout);
|
||||
try {
|
||||
socket.disconnect();
|
||||
} catch {}
|
||||
resolve({ authToken, refreshToken });
|
||||
resolve({ accessToken, refreshToken });
|
||||
}
|
||||
} catch {
|
||||
// ignore parse errors
|
||||
@@ -82,8 +86,13 @@ export const user = {
|
||||
};
|
||||
|
||||
socket.on("connect", () => {});
|
||||
socket.on("auth-callback", handlePayload);
|
||||
socket.on("message", handlePayload);
|
||||
// listen for a specific callback key if provided
|
||||
if (callbackKey) {
|
||||
socket.on(`auth:login:callback:${callbackKey}`, handlePayload);
|
||||
} else {
|
||||
socket.on("auth-callback", handlePayload);
|
||||
}
|
||||
socket.on("message", console.log);
|
||||
socket.on("connect_error", (err: any) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
|
||||
Reference in New Issue
Block a user