Implement Password Grant Flow for Authentication and Enhance User Experience
- Introduced Password Grant Flow for user authentication, allowing direct login with email and password. - Updated `useAuth` composable to manage login and logout processes, including Single Sign-Out from Cidaas. - Enhanced user interface with a new `UserMenu` component displaying user information and logout functionality. - Updated homepage to show personalized greetings for logged-in users and a login prompt for guests. - Added logout confirmation page with a countdown redirect to the homepage. - Documented the implementation details and future enhancements for OAuth2 flows in CLAUDE.md and other relevant documentation. - Added test credentials and guidelines for automated testing in the new TESTING.md file.
This commit is contained in:
124
CLAUDE.md
124
CLAUDE.md
@@ -483,7 +483,129 @@ export async function submitOrderToXAPI(payload: XAPIOrderPayload) {
|
||||
|
||||
See [`docs/CIDAAS_INTEGRATION.md`](./docs/CIDAAS_INTEGRATION.md) for complete Cidaas OAuth2 implementation guide.
|
||||
|
||||
### OAuth2 Login Flow Pattern
|
||||
### Current Implementation: Password Grant Flow (MVP)
|
||||
|
||||
**Important:** The current implementation uses the **Resource Owner Password Credentials Grant** (OAuth2 Password Flow) instead of the Authorization Code Flow with PKCE.
|
||||
|
||||
**Why Password Grant for MVP:**
|
||||
- ✅ **Simpler UX:** User stays in our app, no redirects to Cidaas
|
||||
- ✅ **Faster development:** Less complex flow, fewer endpoints needed
|
||||
- ✅ **Sufficient for MVP:** Private users logging in with email/password
|
||||
- ⚠️ **Trade-off:** Client app handles passwords directly (less secure than authorization code flow)
|
||||
- ⚠️ **Limitation:** Doesn't support SSO/Social logins (requires redirect flow)
|
||||
|
||||
**Future Enhancement:** For Phase 2+, we may implement Authorization Code Flow with PKCE to support:
|
||||
- Social login (Google, Facebook, Apple)
|
||||
- Single Sign-On (SSO) for organizations
|
||||
- Better security (app never sees password)
|
||||
|
||||
### Password Grant Login Pattern (Current Implementation)
|
||||
|
||||
```typescript
|
||||
// app/composables/useAuth.ts - Client-side auth composable
|
||||
export function useAuth() {
|
||||
const { loggedIn, user, clear, fetch } = useUserSession()
|
||||
|
||||
async function login(email: string, password: string) {
|
||||
// Direct login via Password Grant (no redirect)
|
||||
try {
|
||||
await $fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
body: { email, password },
|
||||
})
|
||||
|
||||
// Refresh session data
|
||||
await fetch()
|
||||
|
||||
// Redirect to homepage or intended destination
|
||||
navigateTo('/')
|
||||
} catch (error) {
|
||||
// Handle login error (invalid credentials, etc.)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
await $fetch('/api/auth/logout', { method: 'POST' })
|
||||
await clear()
|
||||
navigateTo('/')
|
||||
}
|
||||
|
||||
return { user, loggedIn, login, logout }
|
||||
}
|
||||
```
|
||||
|
||||
**Server-side:**
|
||||
|
||||
```typescript
|
||||
// server/api/auth/login.post.ts - Password Grant login
|
||||
import { loginWithPassword, fetchUserInfo } from '~/server/utils/cidaas'
|
||||
import { z } from 'zod'
|
||||
|
||||
const loginSchema = z.object({
|
||||
email: z.string().email(),
|
||||
password: z.string().min(1),
|
||||
})
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
// 1. Validate input
|
||||
const body = await readBody(event)
|
||||
const { email, password } = loginSchema.parse(body)
|
||||
|
||||
try {
|
||||
// 2. Authenticate with Cidaas via Password Grant
|
||||
const tokens = await loginWithPassword(email, password)
|
||||
|
||||
// 3. Fetch user info from Cidaas
|
||||
const cidaasUser = await fetchUserInfo(tokens.access_token)
|
||||
|
||||
// 4. Create/update user in local DB
|
||||
const db = useDatabase()
|
||||
let user = await db.query.users.findFirst({
|
||||
where: eq(users.experimentaId, cidaasUser.sub),
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
// First time login - create user
|
||||
const [newUser] = await db
|
||||
.insert(users)
|
||||
.values({
|
||||
experimentaId: cidaasUser.sub,
|
||||
email: cidaasUser.email,
|
||||
firstName: cidaasUser.given_name || '',
|
||||
lastName: cidaasUser.family_name || '',
|
||||
})
|
||||
.returning()
|
||||
user = newUser
|
||||
} else {
|
||||
// Update last login timestamp
|
||||
await db
|
||||
.update(users)
|
||||
.set({ updatedAt: new Date() })
|
||||
.where(eq(users.id, user.id))
|
||||
}
|
||||
|
||||
// 5. Create encrypted session
|
||||
await setUserSession(event, {
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
},
|
||||
})
|
||||
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'Invalid credentials',
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### OAuth2 Authorization Code Flow Pattern (Future Enhancement)
|
||||
|
||||
```typescript
|
||||
// composables/useAuth.ts - Client-side auth composable
|
||||
|
||||
Reference in New Issue
Block a user