Skip to content

Commit 93e2d62

Browse files
committed
feat: update authentication flow and environment configuration
- Update sign-in and sign-up forms to provide more specific error messages for user feedback. - Add BASE_URL environment variable for better handling of deployment paths in Docusaurus. - Refactor URL handling in auth-client and callback components to auto-detect base paths. - Introduce utility functions for managing base URL detection and redirect URI generation. - Update .env.example to include new environment variable documentation. These changes improve user experience during authentication and streamline deployment configurations.
1 parent fa5b0f7 commit 93e2d62

File tree

11 files changed

+132
-36
lines changed

11 files changed

+132
-36
lines changed

.github/workflows/deploy.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ jobs:
5252
# These are read by docusaurus.config.ts via process.env
5353
AUTH_URL: ${{ secrets.AUTH_URL }}
5454
OAUTH_CLIENT_ID: ${{ secrets.OAUTH_CLIENT_ID }}
55+
BASE_URL: ${{ secrets.BASE_URL }}
5556
# Optional: Add other env vars if needed
5657
# GA4_MEASUREMENT_ID: ${{ secrets.GA4_MEASUREMENT_ID }}
5758
run: npm run build

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ node_modules/
1515
.env.development.local
1616
.env.test.local
1717
.env.production.local
18-
18+
.env.prod
1919
npm-debug.log*
2020
yarn-debug.log*
2121
yarn-error.log*
@@ -39,4 +39,4 @@ build/
3939
.coverage
4040
htmlcov/
4141
.mypy_cache/
42-
.ruff_cache/
42+
.ruff_cache/

auth-server/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ INSERT INTO oauth_application (
3939
'robolearn-public-client',
4040
NULL, -- No secret for public client (PKCE only)
4141
'RoboLearn Public Client',
42-
'http://localhost:3000/auth/callback,https://robolearn.github.io/auth/callback', -- Comma-separated: dev + prod URLs
42+
'http://localhost:3000/auth/callback,http://localhost:3000/robolearn/auth/callback,https://mjunaidca.github.io/robolearn/auth/callback', -- Comma-separated: dev (both variants) + prod URLs
4343
'public',
4444
false,
4545
'{"token_endpoint_auth_method":"none","grant_types":["authorization_code","refresh_token"]}',

auth-server/src/components/sign-in-form.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,28 @@ export function SignInForm() {
6363
});
6464

6565
if (result.error) {
66-
// Check if email needs verification (403 status)
67-
if (result.error.status === 403 || result.error.message?.includes("verify")) {
66+
// Log error for debugging (remove in production if needed)
67+
console.error("[SignIn] Error:", result.error.status, result.error.message);
68+
69+
// Check if email needs verification (403 status or message contains verify)
70+
if (result.error.status === 403 || result.error.message?.toLowerCase().includes("verify") || result.error.message?.toLowerCase().includes("verification") || result.error.message?.toLowerCase().includes("not verified")) {
6871
setErrors({
69-
general: "Please verify your email address before signing in.",
72+
general: "Please verify your email address before signing in. Check your inbox for the verification email.",
7073
needsVerification: true,
7174
});
75+
} else if (result.error.message?.toLowerCase().includes("password") || result.error.message?.toLowerCase().includes("incorrect") || result.error.message?.toLowerCase().includes("invalid password")) {
76+
// Wrong password
77+
setErrors({ general: "Invalid password. Please check your password and try again." });
78+
} else if (result.error.message?.toLowerCase().includes("not found") || result.error.message?.toLowerCase().includes("does not exist") || result.error.message?.toLowerCase().includes("no account")) {
79+
// Email not found
80+
setErrors({ general: "No account found with this email. Please sign up first." });
7281
} else {
7382
// Generic error message for security (don't reveal which field is wrong)
74-
setErrors({ general: "Invalid credentials. Please check your email and password." });
83+
// But show the actual error message if it's helpful
84+
const errorMsg = result.error.message || "Invalid credentials. Please check your email and password.";
85+
setErrors({ general: errorMsg });
7586
}
87+
setIsLoading(false);
7688
return;
7789
}
7890

auth-server/src/components/sign-up-form.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,15 @@ export function SignUpForm() {
9999
});
100100

101101
if (result.error) {
102-
if (result.error.message?.includes("already exists")) {
103-
setErrors({ email: "This email is already registered. Try signing in instead." });
102+
if (result.error.message?.includes("already exists") || result.error.message?.includes("already registered")) {
103+
setErrors({
104+
email: "This email is already registered. Try signing in instead.",
105+
general: "An account with this email already exists. Please sign in instead."
106+
});
104107
} else {
105108
setErrors({ general: result.error.message || "Registration failed. Please try again." });
106109
}
110+
setIsLoading(false);
107111
return;
108112
}
109113

robolearn-interface/.env.example

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1-
# Auth server URL
2-
AUTH_URL=http://localhost:3001
3-
4-
# OAuth client ID
5-
# Use the pre-configured public client (PKCE, no secret needed):
6-
OAUTH_CLIENT_ID=robolearn-public-client
7-
# Or register a custom client in the admin panel (/admin/clients)
8-
9-
# Site URL (for OAuth redirects, auto-detected in dev)
10-
# SITE_URL=https://your-domain.com
11-
12-
# Google Analytics (optional)
13-
# GA4_MEASUREMENT_ID=G-XXXXXXXXXX
1+
# Docusaurus Configuration
2+
# Copy this file to .env and update with your values
3+
4+
# Base URL for the site
5+
# For local development, use "/" (default)
6+
# For GitHub Pages deployment, use "/robolearn/"
7+
BASE_URL=/
8+
9+
# Auth Server Configuration
10+
# URL of the authentication server for login/signup redirects
11+
AUTH_URL=http://localhost:3001
12+
13+
# OAuth Client ID
14+
# Pre-configured trusted client (PKCE + JWKS)
15+
# This should match the trustedClients[0].clientId in auth-server
16+
OAUTH_CLIENT_ID=robolearn-public-client
17+
18+
# Google Analytics 4 (Optional)
19+
# Uncomment and set your GA4 Measurement ID to enable analytics
20+
# GA4_MEASUREMENT_ID=G-XXXXXXXXXX

robolearn-interface/docusaurus.config.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ const OAUTH_CLIENT_ID = process.env.OAUTH_CLIENT_ID || "robolearn-public-client"
1616

1717
// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)
1818

19+
// Base URL configuration - defaults to "/" for local development
20+
const BASE_URL = process.env.BASE_URL || "/";
21+
1922
const config: Config = {
2023
title: "RoboLearn",
2124
tagline:
@@ -37,7 +40,9 @@ const config: Config = {
3740
url: "https://mjunaidca.github.io",
3841
// Set the /<baseUrl>/ pathname under which your site is served
3942
// For GitHub pages deployment, it is often '/<projectName>/'
40-
baseUrl: "/robolearn/",
43+
// For local development, use '/' (default)
44+
// For production/GitHub Pages, set BASE_URL=/robolearn/ in environment
45+
baseUrl: BASE_URL,
4146

4247
// Sitemap is configured via the classic preset's sitemap option below
4348

@@ -302,7 +307,7 @@ const config: Config = {
302307
{
303308
type: "custom-navbarAuth",
304309
position: "right",
305-
value: '<a href="/robolearn/docs/preface-agent-native" class="navbar__cta-button">Start Free</a>',
310+
value: `<a href="${BASE_URL}docs/preface-agent-native" class="navbar__cta-button">Start Free</a>`,
306311
},
307312
],
308313
},

robolearn-interface/src/contexts/AuthContext.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
22
import { refreshAccessToken } from '../lib/auth-client';
33
import { verifyIDToken, extractUserFromToken } from '../lib/jwt-verifier';
4+
import { getHomeUrl } from '../lib/url-utils';
45

56
interface User {
67
id: string;
@@ -153,14 +154,18 @@ export function AuthProvider({ children, authUrl, oauthClientId }: AuthProviderP
153154
// Clear session state
154155
setSession(null);
155156

157+
// Get home URL - auto-detects base path from current URL
158+
const homeUrl = getHomeUrl();
159+
156160
if (global) {
157161
// Global logout: redirect to auth server to end session there too
158162
// This logs user out from all apps using this auth server
159-
window.location.href = `${effectiveAuthUrl}/api/auth/sign-out?redirectTo=${encodeURIComponent(window.location.origin)}`;
163+
const redirectTo = typeof window !== 'undefined' ? `${window.location.origin}${homeUrl}` : homeUrl;
164+
window.location.href = `${effectiveAuthUrl}/api/auth/sign-out?redirectTo=${encodeURIComponent(redirectTo)}`;
160165
} else {
161166
// Local logout: just redirect to home
162167
// User stays logged in at auth server (SSO pattern)
163-
window.location.href = '/';
168+
window.location.href = homeUrl;
164169
}
165170
};
166171

robolearn-interface/src/lib/auth-client.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createAuthClient } from "better-auth/react";
2+
import { getRedirectUri } from "./url-utils";
23

34
// Default fallbacks for development
45
const DEFAULT_AUTH_URL = "http://localhost:3001";
@@ -44,16 +45,17 @@ export interface OAuthConfig {
4445
authUrl?: string;
4546
clientId?: string;
4647
redirectUri?: string;
48+
// baseUrl removed - we detect it automatically from current URL
4749
}
4850

4951
// OAuth2 Authorization URL builder with PKCE
5052
// Config should be passed from component using useDocusaurusContext
5153
export async function getOAuthAuthorizationUrl(state?: string, config?: OAuthConfig): Promise<string> {
5254
const authUrl = config?.authUrl || DEFAULT_AUTH_URL;
5355
const clientId = config?.clientId || DEFAULT_CLIENT_ID;
54-
const redirectUri = config?.redirectUri || (typeof window !== 'undefined'
55-
? `${window.location.origin}/auth/callback`
56-
: "http://localhost:3000/auth/callback");
56+
57+
// Generate redirect URI - use provided one, or auto-detect from current URL
58+
const redirectUri = config?.redirectUri || getRedirectUri();
5759

5860
// Generate PKCE code verifier and challenge
5961
const codeVerifier = generateCodeVerifier();
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* URL utilities for handling baseUrl in Docusaurus deployments
3+
* Works for dev, GitHub Pages, and custom domains
4+
*
5+
* Simple approach: Detect the base path from the current URL
6+
* No hardcoding - just use whatever path the user is currently on
7+
*/
8+
9+
/**
10+
* Detects the base path from the current URL
11+
*
12+
* Docusaurus respects baseUrl even in dev mode.
13+
* If baseUrl is "/robolearn/", you access the site at http://localhost:3000/robolearn/
14+
*
15+
* So we detect the base path from the current URL pathname.
16+
*/
17+
function detectBasePath(): string {
18+
if (typeof window === 'undefined') return '';
19+
20+
const pathname = window.location.pathname;
21+
22+
// Extract the first path segment (e.g., /robolearn/docs -> /robolearn)
23+
const match = pathname.match(/^\/([^/]+)/);
24+
25+
if (match) {
26+
const firstSegment = match[1];
27+
// If first segment is a known app/content path, we're at root
28+
// These are Docusaurus routes that don't indicate a baseUrl
29+
// Add custom routes like 'labs', 'chat' to this list
30+
if (['auth', 'api', 'docs', 'blog', 'search', 'labs', 'chat'].includes(firstSegment)) {
31+
return '';
32+
}
33+
// Otherwise, we're in a subpath (e.g., /robolearn/)
34+
return `/${firstSegment}`;
35+
}
36+
37+
return ''; // Root (pathname is just "/")
38+
}
39+
40+
/**
41+
* Gets the home URL (with baseUrl if applicable)
42+
*/
43+
export function getHomeUrl(): string {
44+
const basePath = detectBasePath();
45+
return basePath ? `${basePath}/` : '/';
46+
}
47+
48+
/**
49+
* Constructs a redirect URI for OAuth callback
50+
* Uses the detected base path from current URL - no hardcoding needed
51+
*/
52+
export function getRedirectUri(): string {
53+
if (typeof window === 'undefined') {
54+
return 'http://localhost:3000/auth/callback';
55+
}
56+
57+
const basePath = detectBasePath();
58+
return `${window.location.origin}${basePath}/auth/callback`;
59+
}
60+

0 commit comments

Comments
 (0)