Implement direct login functionality with email and password
- Update login API to support direct authentication via email and password, removing the OAuth2 redirect flow. - Modify LoginForm component to include password field and validation. - Refactor useAuth composable to handle login with both email and password. - Remove unnecessary OAuth2 callback handler and PKCE utilities. - Update relevant documentation and error handling for the new login method.
This commit is contained in:
@@ -218,6 +218,74 @@ export async function registerUser(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Login with username and password (Resource Owner Password Credentials Flow)
|
||||
*
|
||||
* @param email - User email address
|
||||
* @param password - User password
|
||||
* @returns Token response with access_token and id_token
|
||||
* @throws H3Error if login fails
|
||||
*/
|
||||
export async function loginWithPassword(
|
||||
email: string,
|
||||
password: string
|
||||
): Promise<CidaasTokenResponse> {
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
// Prepare token request with password grant
|
||||
const params = new URLSearchParams({
|
||||
grant_type: 'password',
|
||||
username: email, // Cidaas uses 'username' field for email
|
||||
password,
|
||||
client_id: config.cidaas.clientId,
|
||||
client_secret: config.cidaas.clientSecret,
|
||||
scope: 'openid profile email', // Request OIDC scopes
|
||||
})
|
||||
|
||||
try {
|
||||
const response = await fetch(config.cidaas.tokenUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: params.toString(),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
console.error('Cidaas password login failed:', errorData)
|
||||
|
||||
// Handle specific errors
|
||||
if (response.status === 401) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'Invalid email or password',
|
||||
})
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: response.status,
|
||||
statusMessage: 'Login failed',
|
||||
data: errorData,
|
||||
})
|
||||
}
|
||||
|
||||
const tokens: CidaasTokenResponse = await response.json()
|
||||
return tokens
|
||||
} catch (error) {
|
||||
console.error('Password login error:', error)
|
||||
|
||||
if ((error as H3Error).statusCode) {
|
||||
throw error // Re-throw H3Error
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to authenticate with Cidaas',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh access token using refresh token
|
||||
*
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
// server/utils/pkce.ts
|
||||
|
||||
/**
|
||||
* PKCE (Proof Key for Code Exchange) utilities for OAuth2 security.
|
||||
*
|
||||
* PKCE prevents authorization code interception attacks by requiring
|
||||
* the client to prove possession of the original code verifier.
|
||||
*
|
||||
* Flow:
|
||||
* 1. Generate random code_verifier (43-128 chars)
|
||||
* 2. Hash verifier with SHA-256 → code_challenge
|
||||
* 3. Send challenge to authorization server
|
||||
* 4. Server returns authorization code
|
||||
* 5. Exchange code + verifier for tokens
|
||||
* 6. Server validates: SHA256(verifier) === stored_challenge
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generate a random code verifier (43-128 URL-safe characters)
|
||||
*
|
||||
* @param length - Length of verifier (default: 64)
|
||||
* @returns Base64URL-encoded random string
|
||||
*/
|
||||
export function generateCodeVerifier(length: number = 64): string {
|
||||
// Generate random bytes
|
||||
const randomBytes = crypto.getRandomValues(new Uint8Array(length))
|
||||
|
||||
// Convert to base64url (URL-safe base64 without padding)
|
||||
return base64UrlEncode(randomBytes)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SHA-256 hash of code verifier → code challenge
|
||||
*
|
||||
* @param verifier - The code verifier
|
||||
* @returns Base64URL-encoded SHA-256 hash
|
||||
*/
|
||||
export async function generateCodeChallenge(verifier: string): Promise<string> {
|
||||
// Convert verifier string to Uint8Array
|
||||
const encoder = new TextEncoder()
|
||||
const data = encoder.encode(verifier)
|
||||
|
||||
// Hash with SHA-256
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', data)
|
||||
|
||||
// Convert to base64url
|
||||
return base64UrlEncode(new Uint8Array(hashBuffer))
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate PKCE verifier + challenge pair
|
||||
*
|
||||
* @returns Object with verifier and challenge
|
||||
*/
|
||||
export async function generatePKCE(): Promise<{
|
||||
verifier: string
|
||||
challenge: string
|
||||
}> {
|
||||
const verifier = generateCodeVerifier()
|
||||
const challenge = await generateCodeChallenge(verifier)
|
||||
|
||||
return { verifier, challenge }
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Uint8Array to Base64URL string
|
||||
*
|
||||
* Base64URL is URL-safe variant of Base64:
|
||||
* - Replace '+' with '-'
|
||||
* - Replace '/' with '_'
|
||||
* - Remove padding '='
|
||||
*/
|
||||
function base64UrlEncode(buffer: Uint8Array): string {
|
||||
// Convert to base64
|
||||
const base64 = btoa(String.fromCharCode(...buffer))
|
||||
|
||||
// Convert to base64url
|
||||
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random state parameter for CSRF protection
|
||||
*
|
||||
* @param length - Length of state string (default: 32)
|
||||
* @returns Random URL-safe string
|
||||
*/
|
||||
export function generateState(length: number = 32): string {
|
||||
const randomBytes = crypto.getRandomValues(new Uint8Array(length))
|
||||
return base64UrlEncode(randomBytes)
|
||||
}
|
||||
Reference in New Issue
Block a user