Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
564f163
feat: add passkeys registration, auth, verify routes
matteyu Jan 22, 2025
edc5e1e
add auth methods for passkeys
matteyu Jan 24, 2025
4655f75
handle merge conflicts
matteyu Feb 25, 2025
91b3475
add missing userprofile fields
matteyu Feb 25, 2025
1746dbd
fix imports
matteyu Feb 26, 2025
f11d5f0
add deps for erd
matteyu Feb 26, 2025
d1d19ce
skip erd generate on vercel
matteyu Feb 26, 2025
f7ba311
swtich to npx
matteyu Feb 26, 2025
5caa432
comment out prisma erd generator
matteyu Feb 26, 2025
a49eaff
refactor passkeys
matteyu Feb 26, 2025
fde81bd
change to buffer.from
matteyu Feb 26, 2025
2951926
add exported types from prisma
matteyu Feb 26, 2025
88d195f
dry functions for challenge validations
matteyu Feb 26, 2025
1e08ccf
fix merge conflicts
matteyu Feb 27, 2025
e11ff0b
add challenge purpose authentication
matteyu Feb 27, 2025
7f49d71
fix merge conflicts
matteyu Mar 4, 2025
47adcc9
merge conflicts
matteyu Mar 5, 2025
6e8a885
fix merge conflicts
matteyu Mar 10, 2025
f284f19
add apple callback
matteyu Mar 10, 2025
b0e5316
fix merge conflict
matteyu Mar 12, 2025
19f1040
refactor passkeys
matteyu Mar 12, 2025
bf147c6
remove unused auth
matteyu Mar 13, 2025
b6530a9
modify test page
matteyu Mar 20, 2025
df6ed2f
switch to otp
matteyu Mar 24, 2025
e184777
add refresh token test
matteyu Mar 24, 2025
c26f941
Merge branch 'development' of github.com:arconnectio/embed-api into a…
matteyu Mar 25, 2025
2c461d1
add back readme
matteyu Mar 25, 2025
d253050
revert back context session update
matteyu Mar 25, 2025
73cbec6
fix input errors
matteyu Mar 25, 2025
995ee31
remake init migration
matteyu Mar 25, 2025
0bcf1a6
resolve merge confilicts
matteyu Mar 25, 2025
e152969
add back custom access token migration
matteyu Mar 25, 2025
9a7ee95
rename init migration
matteyu Mar 25, 2025
4a6a618
remove countryCode from context
matteyu Mar 25, 2025
8cd440f
fix context errors
matteyu Mar 25, 2025
72cf369
Merge branch 'development' of github.com:arconnectio/embed-api into a…
matteyu Apr 1, 2025
8c3a390
add index
matteyu Apr 2, 2025
845aa21
fix passkey logic
matteyu Apr 8, 2025
bdfc880
implement authentication verification
matteyu Apr 9, 2025
ba37c16
add session metadata
matteyu Apr 9, 2025
fe976e0
modifications for UI integration
matteyu Apr 20, 2025
bb53dfc
fix migration files
matteyu Apr 21, 2025
a3dff5e
fix reuse body in req for custom token
matteyu Apr 21, 2025
d4cdabe
enable for iframe
matteyu Apr 22, 2025
c1f0fe2
fix merge conflicts
matteyu Apr 30, 2025
224925c
use supabase auth for token generation
matteyu May 1, 2025
ac6f774
fix user trigger
matteyu May 2, 2025
70153a5
cleanup crossauth
matteyu May 2, 2025
cfad026
fix session data structure
matteyu May 6, 2025
0c4438b
restore x-custom-auth
matteyu May 6, 2025
626ecf3
fix merge conflicts
matteyu May 6, 2025
34b3e8d
modify passkey appraoch
matteyu May 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ BACKUP_FILE_PUBLIC_KEY=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoVLU6+xg0/joE
# Temporary:
NEXT_PUBLIC_APPLICATION_ID=""
NEXT_PUBLIC_CLIENT_ID=""

# Passkeys:
SIGNUP_EMAIL_ADDRESS="" # email address without the domain
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ next-env.d.ts

## supabase
/supabase/
.qodo
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,4 @@ pnpm db:regenerate-migrations

If this worked, you should see 5 triggers in Supabase under [Database > Triggers > auth](https://supabase.com/dashboard/project/pboorlggoqpyiucxmneq/database/triggers?schema=auth).

Also, make sure you delete your Supabase users under Authentication, as those are no longer duplicated in the `UserProfile` table.
Also, make sure you delete your Supabase users under Authentication, as those are no longer duplicated in the `UserProfile` table.
76 changes: 76 additions & 0 deletions app/auth/callback/apple/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"use client";

import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { supabase } from "@/client/utils/supabase/supabase-client-client"

const redirectToHash = (path: string, params?: Record<string, string>) => {
const baseUrl = window.location.origin;
let url = `${baseUrl}/#${path}`;

// Add query params if provided
if (params) {
const queryString = Object.entries(params)
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join('&');
url += `?${queryString}`;
}

console.log("Redirecting to:", url);
window.location.replace(url);
};

export default function AppleAuthCallback() {
const router = useRouter();

useEffect(() => {
const handleAuthCallback = async () => {
try {
console.log("Processing Apple OAuth callback");

// Get the auth code from the URL
const { data, error } = await supabase.auth.exchangeCodeForSession(
window.location.href
);

if (error) {
console.error("Error exchanging code for session:", error);
redirectToHash('/', { error: "Authentication failed" });
return;
}

console.log("Apple authentication successful", data);

// Save the session token in localStorage
if (data.session?.access_token) {
localStorage.setItem('authToken', data.session.access_token);
// Make sure we don't have any conflicting auth flags
localStorage.removeItem('isCustomAuth');

// We can also store the refresh token if available
if (data.session.refresh_token) {
localStorage.setItem('refreshToken', data.session.refresh_token);
}
}

// Redirect to the auth/restore-shares path after successful authentication
redirectToHash('/auth/restore-shares');
} catch (error) {
console.error("Error during Apple authentication callback:", error);
// Redirect to login page if there's an error
redirectToHash('/', { error: "Authentication failed" });
}
};

handleAuthCallback();
}, [router]);

return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-center">
<h2 className="text-2xl font-semibold mb-4">Authenticating with Apple...</h2>
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-gray-900 mx-auto"></div>
</div>
</div>
);
}
126 changes: 116 additions & 10 deletions app/auth/callback/google/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,131 @@ import { useEffect } from "react"
import { useRouter } from "next/navigation"
import { supabase } from "../../../../client/utils/supabase/supabase-client-client"

const redirectToHash = (path: string, params?: Record<string, string>) => {
const baseUrl = window.location.origin;
let url = `${baseUrl}/#${path}`;

// Add query params if provided
if (params) {
const queryString = Object.entries(params)
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join('&');
url += `?${queryString}`;
}

console.log("Redirecting to:", url);
window.location.replace(url);
};

export default function AuthCallbackPage() {
const router = useRouter()

useEffect(() => {
const handleAuthCallback = async () => {
const { data, error } = await supabase.auth.getSession()
if (error) {
console.error("Error during auth callback:", error)
router.push("/login?error=Unable to authenticate")
} else if (data.session) {
router.push("/dashboard")
} else {
router.push("/login?error=No session found")
try {
console.log("Processing Google OAuth callback");
console.log("Current URL:", window.location.href);
console.log("Origin:", window.location.origin);

// Add protection for double processing
const urlParams = new URLSearchParams(window.location.search);
const hashParams = new URLSearchParams(window.location.hash.replace('#', ''));
const codeParam = urlParams.get('code') || hashParams.get('code');

if (!codeParam) {
console.warn("No code parameter found in URL, cannot exchange for session");
redirectToHash('/login', { error: "Missing authentication code" });
return;
}

// Try the code exchange
try {
console.log("Attempting to exchange code for session...");
const { data, error } = await supabase.auth.exchangeCodeForSession(window.location.href);

if (error) {
console.error("Error exchanging code for session:", error);
// Redirect to login with error using hash-based format
redirectToHash('/login', { error: "Unable to authenticate: " + error.message });
return;
}

console.log("Authentication successful, session data:",
data.session ? {
hasAccessToken: !!data.session.access_token,
hasRefreshToken: !!data.session.refresh_token,
expiresAt: data.session.expires_at,
user: data.session.user ? {
id: data.session.user.id,
email: data.session.user.email
} : null
} : "No session"
);

// Save the session token in localStorage
if (data.session?.access_token) {
console.log("Storing access token in localStorage");
localStorage.setItem('authToken', data.session.access_token);

// Make sure we don't have any conflicting auth flags
localStorage.removeItem('isCustomAuth');

// We can also store the refresh token if available
if (data.session.refresh_token) {
localStorage.setItem('refreshToken', data.session.refresh_token);
}

// Additional information for debugging
localStorage.setItem('userId', data.session.user?.id || '');

// For debugging, also log any other session properties
console.log("Session expires at:", data.session.expires_at ?
new Date(data.session.expires_at * 1000).toLocaleString() :
"No expiration time"
);

// Force a delay before redirecting to ensure localStorage is updated
await new Promise(resolve => setTimeout(resolve, 500));

// Detect if we're in the extension context (port 5173)
const isExtension = window.location.origin.includes('localhost:5173') ||
window.location.origin.includes('chrome-extension://');

console.log("Is extension context:", isExtension);

// Successfully authenticated, redirect to auth/restore-shares using hash-based format
// Make sure we're using the right format for the extension
if (isExtension) {
// For extension, use a simpler redirect format
console.log("Using extension redirect format");
window.location.replace(`${window.location.origin}/#/auth/restore-shares`);
} else {
// For web app, use the standard format
console.log("Using web app redirect format");
redirectToHash('/auth/restore-shares');
}
} else {
console.error("No access token in session data");
redirectToHash('/login', { error: "No access token received" });
}
} catch (exchangeError: unknown) {
console.error("Exception during code exchange:", exchangeError);
const errorMessage = exchangeError instanceof Error ? exchangeError.message : "Unknown error";
redirectToHash('/login', { error: "Error during authentication: " + errorMessage });
}
} catch (err) {
console.error("Error during auth callback:", err);
// Redirect to login with error using hash-based format
redirectToHash('/login', { error: "Authentication process failed" });
}
}

handleAuthCallback()
// Delay slightly to ensure DOM is fully loaded
setTimeout(() => {
handleAuthCallback();
}, 100);
}, [router])

return <div>Processing authentication...</div>
return <div>Processing Google authentication...</div>
}

22 changes: 10 additions & 12 deletions app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { trpc } from "@/client/utils/trpc/trpc-client"
import { ProtectedApiInteraction } from "../../client/components/ProtectedApiInteraction"
import { useAuth } from "@/client/hooks/useAuth"
import { supabase } from "@/client/utils/supabase/supabase-client-client"
import RefreshTokenTest from "@/client/components/RefreshTokenTest"

export default function DashboardPage() {
const router = useRouter();
Expand All @@ -19,19 +20,16 @@ export default function DashboardPage() {
}
}, [isAuthLoading, user, router])

const handleRefresh = async () => {
await supabase.auth.refreshSession();
}

const handleLogout = async () => {
try {
setIsLoading(true);

await logoutMutation.mutateAsync();
await supabase.auth.signOut();

router.push("/");
} catch (error) {
setIsLoading(false);

console.error("Logout failed:", error)
}
}
Expand All @@ -43,13 +41,6 @@ export default function DashboardPage() {
<div className="container mx-auto px-4 py-8">
<div className="flex justify-between items-center mb-8">
<h1 className="text-3xl font-bold">Dashboard</h1>
<button
onClick={handleRefresh}
className="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
>
Refresh Session
</button>

<button
onClick={handleLogout}
className="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
Expand All @@ -58,6 +49,13 @@ export default function DashboardPage() {
</button>
</div>
<p className="mb-4">Welcome, user with ID: {user.id}</p>

{/* Include the RefreshTokenTest component */}
<div className="mb-8">
<h2 className="text-xl font-bold mb-4">Session Management</h2>
<RefreshTokenTest />
</div>

<ProtectedApiInteraction />
</div>
)
Expand Down
6 changes: 6 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

/* Your custom styles below */

body {
font-family: Arial, Helvetica, sans-serif;
}
Loading