diff --git a/README.md b/README.md index ef2049e..da7c53b 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,21 @@ # @aieng-auth -> Production-ready Google OAuth SSO for all your web applications +Production-ready Google OAuth SSO for Vector internal web applications. -Seamless single sign-on across multiple apps using **one shared Google OAuth client**. Perfect for organizations that want to add authentication to internal tools with minimal configuration. +## Key Features -## đŸŽ¯ Key Features +- Share one Google OAuth client across multiple apps for seamless SSO +- Client-side OAuth with PKCE security (no backend required) +- Domain restriction by email (e.g., @vectorinstitute.ai) +- React hooks and components with full TypeScript support -- **Single OAuth Client, Multiple Apps** - All your apps share one Google OAuth client for seamless SSO -- **3-Step Integration** - Install, wrap, configure. That's it. -- **Domain Restriction** - Restrict access to specific email domains (e.g., @vectorinstitute.ai) -- **Zero Backend Required** - Pure client-side OAuth with PKCE security -- **Framework Support** - React hooks + components (Next.js coming soon) -- **TypeScript First** - Fully typed for excellent DX +## Packages -## đŸ“Ļ Packages +- `@aieng-auth/core` - Framework-agnostic OAuth client with PKCE +- `@aieng-auth/react` - React hooks and components (AuthProvider, useAuth, ProtectedRoute) +- `demo-react` - Demo application -- **`@aieng-auth/core`** ✅ - Framework-agnostic OAuth client with PKCE -- **`@aieng-auth/react`** ✅ - React hooks and components (AuthProvider, useAuth, ProtectedRoute) -- **`demo-react`** ✅ - Live demo application - -## 🚀 Quick Start +## Quick Start ### 1. Install @@ -68,7 +64,7 @@ function MyComponent() { } ``` -## đŸ—ī¸ Architecture: Single OAuth Client Model +## Architecture ``` ┌─────────────────────────────────────────────┐ @@ -96,28 +92,22 @@ function MyComponent() { Seamless SSO across all apps ``` -**For Developers**: Adding auth to a new app is simple: +All apps share one OAuth client. To add auth to a new app: 1. Get the shared client ID from your admin -2. Ask admin to register your redirect URI -3. Install package and configure (2 env vars) -4. Done! - -## 🔧 Setup Google OAuth +2. Ask admin to add your redirect URI to the OAuth client +3. Install and configure with environment variables -### One-Time Admin Setup +## Setup Google OAuth -1. Go to [Google Cloud Console](https://console.cloud.google.com/) -2. Create a project or select existing -3. Enable Google+ API -4. Create OAuth 2.0 credentials: - - Application type: **Web application** - - Authorized redirect URIs: Add all your app callback URLs -5. Copy the Client ID +### Admin Setup (One-Time) -### Per-App Setup +1. Create OAuth 2.0 credentials in [Google Cloud Console](https://console.cloud.google.com/) +2. Set application type to Web application +3. Add all app callback URLs to Authorized redirect URIs +4. Share the Client ID with developers -Each developer just needs: +### Developer Setup ```bash # .env @@ -126,41 +116,27 @@ VITE_REDIRECT_URI=http://localhost:3000/callback VITE_ALLOWED_DOMAINS=vectorinstitute.ai ``` -## 📖 Demo +## Demo -See the live demo in `apps/demo-react`: +Run the demo app: ```bash cd apps/demo-react -cp .env.example .env -# Add your Google OAuth Client ID +cp .env.example .env # Add your Google OAuth Client ID pnpm dev ``` -## 🔒 Security +## Security -- **PKCE (Proof Key for Code Exchange)** - Prevents authorization code interception -- **SHA-256 Challenge** - Cryptographically secure -- **Memory Storage (default)** - XSS-immune token storage -- **Domain Validation** - Restrict access by email domain -- **Automatic Token Refresh** - Seamless session management +- PKCE (Proof Key for Code Exchange) with SHA-256 challenge +- Memory storage (XSS-immune) +- Domain validation for email restrictions +- Automatic token refresh -## đŸ› ī¸ Development +## Development ```bash -# Install dependencies -pnpm install - -# Build all packages -pnpm build - -# Run tests -pnpm test - -# Run demo -cd apps/demo-react && pnpm dev +pnpm install # Install dependencies +pnpm build # Build all packages +pnpm test # Run tests ``` - -## 📝 License - -MIT diff --git a/apps/demo-nextjs/src/app/dashboard/page.tsx b/apps/demo-nextjs/src/app/dashboard/page.tsx index 68ce45b..946a0b6 100644 --- a/apps/demo-nextjs/src/app/dashboard/page.tsx +++ b/apps/demo-nextjs/src/app/dashboard/page.tsx @@ -15,8 +15,8 @@ export default async function DashboardPage() { return (
-

📊 Dashboard

-

Protected page - server-side authentication

+

Dashboard

+

Protected page with server-side authentication

@@ -27,13 +27,12 @@ export default async function DashboardPage() {
-

Welcome, {user?.name}!

+

Welcome, {user?.name}

- This page is protected by server-side middleware. You can only see it because you're - authenticated. + This page is protected by server-side middleware.

-
✅ Server-Side Authentication Active
+
Server-side authentication active

Session Information

@@ -42,17 +41,6 @@ export default async function DashboardPage() {
- -
-

💡 Next.js Benefits

- -
); } diff --git a/apps/demo-nextjs/src/app/globals.css b/apps/demo-nextjs/src/app/globals.css index 4b3bc1a..748ffe1 100644 --- a/apps/demo-nextjs/src/app/globals.css +++ b/apps/demo-nextjs/src/app/globals.css @@ -1,3 +1,5 @@ +@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap'); + * { box-sizing: border-box; padding: 0; @@ -11,12 +13,10 @@ body { } body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; + font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + background: linear-gradient(135deg, #EB088A 0%, #313CFF 100%); min-height: 100vh; padding: 2rem; } @@ -74,7 +74,7 @@ body { } .button { - background: #667eea; + background: #EB088A; color: white; border: none; border-radius: 8px; @@ -86,22 +86,22 @@ body { } .button:hover { - background: #5568d3; + background: #d10779; transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); + box-shadow: 0 4px 12px rgba(235, 8, 138, 0.3); } .button.secondary { - background: #6c757d; + background: #000000; } .button.secondary:hover { - background: #5a6268; + background: #333333; } .info-box { background: #f8f9fa; - border-left: 4px solid #667eea; + border-left: 4px solid #EB088A; padding: 1rem; margin: 1rem 0; border-radius: 4px; diff --git a/apps/demo-nextjs/src/app/page.tsx b/apps/demo-nextjs/src/app/page.tsx index d49a2d6..2a4060d 100644 --- a/apps/demo-nextjs/src/app/page.tsx +++ b/apps/demo-nextjs/src/app/page.tsx @@ -12,24 +12,23 @@ export default async function HomePage() { return (
-

🔐 Google OAuth SSO Demo (Next.js)

-

Server-side sessions with App Router

+

Google OAuth SSO Demo (Next.js)

+

Server-side authentication with App Router

{!isAuthenticated ? ( <>
-

Welcome!

+

Welcome

- This demo shows server-side OAuth with Next.js App Router. Unlike the React SPA, - tokens are stored securely in HTTP-only cookies. + Server-side OAuth with HTTP-only cookies for secure token storage.

- ✨ Next.js Integration: + Features:
  • Server-side session management
  • -
  • HTTP-only cookies (XSS immune)
  • +
  • HTTP-only cookies (XSS protection)
  • API routes for OAuth flow
  • Middleware for protected routes
@@ -40,30 +39,26 @@ export default async function HomePage() {
-

🔒 More Secure

-

Tokens never touch the browser. Stored in HTTP-only cookies on the server.

+

Secure Storage

+

Tokens stored in HTTP-only cookies, never exposed to browser.

-

⚡ SSR Ready

-

- Authentication works with Server Components. No client-side JavaScript required. -

+

Server Components

+

Authentication integrated with Next.js Server Components.

-

đŸŽ¯ Production Ready

-

Built for production with session management and automatic token refresh.

+

Auto Refresh

+

Session management with automatic token refresh.

) : ( <>
-

✅ Authenticated

-

- You're signed in with server-side session management! -

+

Authenticated

+

Server-side session active.

{user?.picture && ( @@ -93,23 +88,20 @@ export default async function HomePage() {
- +
-

🎨 How This Works

+

Authentication Flow

    -
  • User clicks "Sign in with Google"
  • -
  • Server initiates OAuth flow with PKCE
  • -
  • - Google redirects to /api/auth/callback -
  • -
  • Server exchanges code for tokens
  • -
  • Tokens stored in encrypted session cookie
  • -
  • Server components can access session directly
  • +
  • Server initiates OAuth with PKCE
  • +
  • Redirect to Google for authorization
  • +
  • Exchange code for tokens at callback
  • +
  • Store tokens in encrypted session cookie
  • +
  • Server components access session directly
diff --git a/apps/demo-react/SETUP_GCLOUD.md b/apps/demo-react/SETUP_GCLOUD.md index 858ab14..eadbf65 100644 --- a/apps/demo-react/SETUP_GCLOUD.md +++ b/apps/demo-react/SETUP_GCLOUD.md @@ -17,7 +17,7 @@ open "https://console.cloud.google.com/apis/credentials?project=coderd" 2. Select **"Internal"** (for @vectorinstitute.ai users only) - This restricts access to your Google Workspace organization 3. Fill in required fields: - - **App name**: `AIEng Auth Demo` + - **App name**: `aieng-auth demo` - **User support email**: Your email - **Developer contact**: Your email 4. Click **"Save and Continue"** @@ -33,7 +33,7 @@ open "https://console.cloud.google.com/apis/credentials/oauthclient?project=code ``` 1. **Application type**: Select **"Web application"** -2. **Name**: `AIEng Auth Demo` +2. **Name**: `aieng-auth demo` 3. **Authorized JavaScript origins**: - Add: `http://localhost:3000` 4. **Authorized redirect URIs**: diff --git a/apps/demo-react/index.html b/apps/demo-react/index.html index 422f563..b5a6fd8 100644 --- a/apps/demo-react/index.html +++ b/apps/demo-react/index.html @@ -3,7 +3,7 @@ - CyberArk Auth Demo + aieng-auth demo
diff --git a/apps/demo-react/src/index.css b/apps/demo-react/src/index.css index 271ccf1..2c4c41e 100644 --- a/apps/demo-react/src/index.css +++ b/apps/demo-react/src/index.css @@ -1,3 +1,5 @@ +@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap'); + * { margin: 0; padding: 0; @@ -5,11 +7,10 @@ } body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + background: linear-gradient(135deg, #EB088A 0%, #313CFF 100%); min-height: 100vh; } @@ -64,28 +65,31 @@ body { } .button { - background: #667eea; + background: #EB088A; color: white; border: none; padding: 0.75rem 1.5rem; border-radius: 8px; font-size: 1rem; + font-weight: 600; cursor: pointer; - transition: background 0.3s; + transition: all 0.3s; margin-right: 1rem; margin-bottom: 0.5rem; } .button:hover { - background: #5568d3; + background: #d10779; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(235, 8, 138, 0.3); } .button.secondary { - background: #764ba2; + background: #000000; } .button.secondary:hover { - background: #653c8a; + background: #333333; } .button.danger { @@ -166,8 +170,9 @@ body { } .feature-card h3 { - color: #667eea; + color: #EB088A; margin-bottom: 0.75rem; + font-weight: 600; } .feature-card p { diff --git a/apps/demo-react/src/pages/dashboard-page.tsx b/apps/demo-react/src/pages/dashboard-page.tsx index b8128ef..e40daf7 100644 --- a/apps/demo-react/src/pages/dashboard-page.tsx +++ b/apps/demo-react/src/pages/dashboard-page.tsx @@ -21,8 +21,8 @@ export default function DashboardPage() { return (
-

📊 Dashboard

-

Protected page - only accessible when authenticated

+

Dashboard

+

Protected page

@@ -35,12 +35,9 @@ export default function DashboardPage() {
-

Welcome, {user?.name}!

-

- This is a protected route. You can only see this page because you're authenticated. -

+

Welcome, {user?.name}

-
✅ Authentication Active
+
Authentication active

User Information

@@ -49,23 +46,6 @@ export default function DashboardPage() {
- -
-

💡 In Your Real App

-
    -
  • - Use useAuth() hook to access user and auth state -
  • -
  • - Use useToken() hook to get access token for API calls -
  • -
  • - Wrap protected routes with <ProtectedRoute> component -
  • -
  • Tokens automatically refresh before expiry
  • -
  • User stays logged in across page refreshes (if using persistent storage)
  • -
-
); } diff --git a/apps/demo-react/src/pages/demo-page.tsx b/apps/demo-react/src/pages/demo-page.tsx index 150cb19..1629d98 100644 --- a/apps/demo-react/src/pages/demo-page.tsx +++ b/apps/demo-react/src/pages/demo-page.tsx @@ -75,8 +75,8 @@ export default function DemoPage() { return (
-

🔐 Interactive Demo

-

Test the CyberArk Auth core package

+

Interactive Demo

+

Test the aieng-auth core package

@@ -98,7 +98,7 @@ export default function DemoPage() { {pkce && (
-
✅ PKCE Generated
+
PKCE Generated
                 {JSON.stringify(
@@ -153,9 +153,9 @@ export default function DemoPage() {
             
           

- {storageType === 'memory' && '🔒 Most secure - tokens lost on refresh'} - {storageType === 'session' && 'âš ī¸ Persists during session - XSS vulnerable'} - {storageType === 'local' && 'âš ī¸ Persists across sessions - least secure'} + {storageType === 'memory' && 'Most secure - tokens lost on refresh'} + {storageType === 'session' && 'Persists during session - XSS vulnerable'} + {storageType === 'local' && 'Persists across sessions - least secure'}

@@ -173,7 +173,7 @@ export default function DemoPage() { {tokens && (
-
✅ Tokens Available
+
Tokens Available
                 {JSON.stringify(
@@ -195,8 +195,7 @@ export default function DemoPage() {
               Token Validation:
               

- Expired:{' '} - {isTokenExpired(tokens.accessToken) ? '❌ Yes' : '✅ No'} + Expired: {isTokenExpired(tokens.accessToken) ? 'Yes' : 'No'}

Decoded Claims: @@ -211,14 +210,14 @@ export default function DemoPage() { {!tokens && (

-
â„šī¸ No tokens stored
+
No tokens stored
)}
{/* Info Section */}
-

📚 Next Steps

+

Next Steps

  • For Real OAuth: Configure .env with your Google OAuth credentials diff --git a/apps/demo-react/src/pages/home-page.tsx b/apps/demo-react/src/pages/home-page.tsx index 9f3cd40..f537fbb 100644 --- a/apps/demo-react/src/pages/home-page.tsx +++ b/apps/demo-react/src/pages/home-page.tsx @@ -18,8 +18,8 @@ export default function HomePage() { return (
    -

    🔐 Google OAuth SSO Demo

    -

    Seamless authentication across all your apps

    +

    Google OAuth SSO Demo

    +

    Production-ready authentication for internal applications

    {error && ( @@ -32,14 +32,13 @@ export default function HomePage() { {!isAuthenticated ? ( <>
    -

    Welcome!

    +

    Welcome

    - This demo shows how simple it is to add Google OAuth SSO to your app using{' '} - @aieng-auth/react + Add Google OAuth SSO using @aieng-auth/react

    - ✨ Integration is 3 steps: + Quick Start:
    1. Install: pnpm add @aieng-auth/react @@ -47,50 +46,37 @@ export default function HomePage() {
    2. Wrap app with <AuthProvider>
    3. -
    4. - Set env vars: CLIENT_ID and REDIRECT_URI -
    5. +
    6. Configure CLIENT_ID and REDIRECT_URI
    -

    đŸŽ¯ Single OAuth Client

    -

    - All your apps share one Google OAuth client. Add new apps by just registering their - redirect URIs. -

    +

    Single OAuth Client

    +

    Share one Google OAuth client across multiple apps for seamless SSO.

    -

    🔒 Domain Restriction

    -

    - Restrict access to @vectorinstitute.ai emails. Only authorized users can sign in. -

    +

    Domain Restriction

    +

    Restrict access by email domain for authorized users only.

    -

    ⚡ Zero Backend

    -

    - Pure client-side with PKCE security. No server needed for OAuth flow. Deploy - anywhere. -

    +

    Client-side OAuth

    +

    PKCE security with no backend required.

    ) : ( <>
    -

    ✅ Authenticated

    -

    - You're signed in! This authentication works across all apps using the same OAuth - client. -

    +

    Authenticated

    +

    Signed in successfully.

    {user?.picture && ( @@ -120,7 +106,7 @@ export default function HomePage() {
    - +
    -

    🎨 How This Works

    +

    Authentication Flow

      -
    • User clicks "Sign in with Google"
    • -
    • Redirects to Google OAuth (with PKCE challenge)
    • -
    • User authorizes and redirects back to /callback
    • -
    • Exchanges code for tokens (with PKCE verifier)
    • -
    • Validates email domain (if configured)
    • -
    • Stores tokens and user info in context
    • -
    • App can now make authenticated requests
    • +
    • Initiate OAuth with PKCE challenge
    • +
    • Redirect to Google for authorization
    • +
    • Exchange authorization code for tokens
    • +
    • Validate email domain
    • +
    • Store session and user info
    diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index fd10840..362ab16 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -2,7 +2,7 @@ "name": "@aieng-auth/eslint-config", "version": "0.1.0", "private": true, - "description": "Shared ESLint configuration for AIEng Auth packages", + "description": "Shared ESLint configuration for aieng-auth packages", "main": "index.js", "files": [ "index.js"