danwel - Advanced Authentication System

Comprehensive authentication methods including OAuth, WebAuthn, 2FA, and magic links.

---

Table of Contents



1. [Overview](#overview)
2. [Authentication Methods](#authentication-methods)
3. [OAuth Providers](#oauth-providers)
4. [WebAuthn/Passkeys](#webauthnpasskeys)
5. [Two-Factor Authentication](#two-factor-authentication)
6. [Magic Link Authentication](#magic-link-authentication)
7. [Mobile App Authentication](#mobile-app-authentication)
8. [Session Management](#session-management)
9. [External App Authorization](#external-app-authorization)
10. [Security Features](#security-features)

---

Overview

danwel implements a sophisticated multi-method authentication system that provides users with flexible, secure login options while maintaining strong security standards.

Supported Authentication Methods



  • • **OAuth Providers** - Google, GitHub, Apple

  • • **WebAuthn/Passkeys** - Biometric and hardware key authentication

  • • **Two-Factor Authentication** - TOTP and email-based 2FA

  • • **Magic Links** - Email-based passwordless authentication

  • • **Traditional** - Email/password with bcrypt hashing

  • • **Mobile Tokens** - One-time tokens for external app authentication


  • Technology Stack



  • • **Laravel Fortify** - Base authentication and 2FA

  • • **Laravel Socialite** - OAuth provider integration

  • • **WebAuthn** - Passkey and hardware key support

  • • **Laravel Sanctum** - API token management
  • ---

    Authentication Methods



    User Authentication Strategy

    Users can have multiple authentication methods enabled simultaneously. The system prioritizes methods in this order:

    1. **OAuth methods** (Google, GitHub, Apple)
    2. **WebAuthn/Passkeys**
    3. **Email/Password** (with or without 2FA)

    Method Detection



    // Get user's available authentication methods
    $methods = $user->getAuthenticationMethods();
    // Returns: ['google', 'passkey', 'email'] etc.

    // Get primary authentication method
    $primaryMethod = $user->getAuthenticationMethod();
    // Returns: 'google', 'github', 'passkey', 'email'

    // Check specific methods
    $user->hasOAuthMethods(); // true if any OAuth method
    $user->hasPasskeys(); // true if WebAuthn configured
    $user->hasIntentionalPassword(); // true if password explicitly set

    ---

    OAuth Providers



    Supported Providers



    | Provider | Scope | User Data Retrieved |
    |----------|-------|---------------------|
    | **Google** | openid profile email | Name, email, avatar, Google ID |
    | **GitHub** | user:email | Name, email, avatar, GitHub ID |
    | **Apple** | name email | Name, email, Apple ID |

    OAuth Configuration



    // config/services.php
    'google' => [
    'client_id' => env('GOOGLE_CLIENT_ID'),
    'client_secret' => env('GOOGLE_CLIENT_SECRET'),
    'redirect' => env('GOOGLE_REDIRECT_URI'),
    ],

    'github' => [
    'client_id' => env('GITHUB_CLIENT_ID'),
    'client_secret' => env('GITHUB_CLIENT_SECRET'),
    'redirect' => env('GITHUB_REDIRECT_URI'),
    ],

    'apple' => [
    'client_id' => env('APPLE_CLIENT_ID'),
    'client_secret' => env('APPLE_CLIENT_SECRET'),
    'redirect' => env('APPLE_REDIRECT_URI'),
    ],


    OAuth Flow



    1. **Initiate** - User clicks OAuth provider button
    2. **Redirect** - User sent to provider authorization page
    3. **Callback** - Provider returns with authorization code
    4. **Token Exchange** - Exchange code for user information
    5. **Account Linking** - Link to existing account or create new user
    6. **Authentication** - User is logged in with session/token

    OAuth Implementation



    // OAuth login routes
    Route::get('/auth/{provider}', [AuthController::class, 'redirectToProvider']);
    Route::get('/auth/{provider}/callback', [AuthController::class, 'handleProviderCallback']);

    // Handle OAuth callback
    public function handleProviderCallback($provider)
    {
    $providerUser = Socialite::driver($provider)->user();

    // Find or create user
    $user = User::updateOrCreate([
    $provider . '_id' => $providerUser->getId(),
    ], [
    'name' => $providerUser->getName(),
    'email' => $providerUser->getEmail(),
    // Avatar handling, etc.
    ]);

    Auth::login($user);
    return redirect()->intended('/dashboard');
    }

    ---

    WebAuthn/Passkeys



    WebAuthn Implementation

    danwel uses the laragear/webauthn package for passkey support:

    // User model implements WebAuthnAuthenticatable
    class User extends Authenticatable implements WebAuthnAuthenticatable
    {
    use WebAuthnAuthentication;

    // Check if user has passkeys
    public function hasPasskeys(): bool
    {
    return $this->webAuthnCredentials()->exists();
    }
    }


    Passkey Registration Flow



    1. **Initiate Registration** - User requests to add passkey
    2. **Challenge Generation** - Server generates cryptographic challenge
    3. **Authenticator Response** - Device generates credential
    4. **Verification** - Server verifies and stores credential
    5. **Confirmation** - User receives confirmation

    Passkey Authentication Flow



    1. **Request Authentication** - User selects passkey option
    2. **Challenge Generation** - Server generates authentication challenge
    3. **User Gesture** - User provides biometric/PIN verification
    4. **Response Verification** - Server verifies signature
    5. **Login Complete** - User is authenticated

    WebAuthn Routes



    // WebAuthn routes (provided by package)
    Route::middleware(['web', 'auth'])->group(function () {
    Route::get('/webauthn/register/options', ...);
    Route::post('/webauthn/register', ...);
    Route::post('/webauthn/login/options', ...);
    Route::post('/webauthn/login', ...);
    });

    ---

    Two-Factor Authentication



    2FA Methods

    danwel supports multiple 2FA methods:

    1. **TOTP Apps** - Google Authenticator, Authy, 1Password
    2. **Email Codes** - Backup method when TOTP unavailable

    TOTP Implementation



    // Enable 2FA for user
    $user->enableTwoFactorAuthentication();

    // Generate QR code for TOTP app
    $qrCode = $user->twoFactorQrCodeSvg();

    // Verify TOTP code
    $isValid = $user->confirmTwoFactorAuthentication($code);

    // Generate recovery codes
    $recoveryCodes = $user->recoveryCodes();


    Email 2FA

    Email-based 2FA provides a backup when TOTP is unavailable:

    // Send email verification code
    $code = random_int(100000, 999999);
    session(['2fa_email_code' => $code, '2fa_expires' => now()->addMinutes(10)]);

    Mail::send(new TwoFactorCodeMail($user, $code));

    2FA Middleware



    // Ensure 2FA is completed
    Route::middleware(['auth', 'verified', 'two-factor'])->group(function () {
    // Protected routes
    });

    ---



    Email Magic Links

    Users can log in via email without passwords:

    // Generate magic link
    public function sendMagicLink(Request $request)
    {
    $user = User::where('email', $request->email)->first();

    if ($user) {
    $token = Str::random(60);

    // Store token with expiration
    Cache::put("magic_link_{$token}", $user->id, 900); // 15 minutes

    // Send email with magic link
    Mail::send(new MagicLinkMail($user, $token));
    }

    return response()->json(['message' => 'Magic link sent']);
    }

    // Authenticate via magic link
    public function authenticateViaMagicLink($token)
    {
    $userId = Cache::pull("magic_link_{$token}");

    if ($userId) {
    $user = User::find($userId);
    Auth::login($user);
    return redirect()->intended('/dashboard');
    }

    return redirect()->route('login')->withErrors(['token' => 'Invalid or expired link']);
    }


    Magic Link Security



  • • **Single Use** - Tokens are deleted after use

  • • **Time Limited** - Links expire after 15 minutes

  • • **Secure Generation** - Cryptographically secure random tokens

  • • **Rate Limited** - Prevent spam requests
  • ---

    Mobile App Authentication



    One-Time Login Tokens

    For mobile apps or external integrations:

    // Generate mobile login token
    Route::post('/auth/mobile-token', [MobileAuthController::class, 'generateLoginToken'])
    ->middleware('throttle:5,1');

    public function generateLoginToken(Request $request)
    {
    $user = auth()->user();

    // Generate secure token
    $token = Str::random(32);

    // Store with short expiration (5 minutes)
    Cache::put("mobile_token_{$token}", [
    'user_id' => $user->id,
    'created_at' => now(),
    ], 300);

    return response()->json(['token' => $token]);
    }

    // Exchange token for API token
    public function exchangeTokenForApiAccess(Request $request)
    {
    $tokenData = Cache::pull("mobile_token_{$request->token}");

    if ($tokenData) {
    $user = User::find($tokenData['user_id']);
    $apiToken = $user->createToken('mobile_app')->plainTextToken;

    return response()->json(['api_token' => $apiToken]);
    }

    return response()->json(['error' => 'Invalid token'], 401);
    }


    Mobile Flow



    1. **User Authentication** - User logs into web app
    2. **Token Generation** - Web app generates one-time token
    3. **Token Display** - QR code or manual entry
    4. **Mobile Exchange** - Mobile app exchanges token for API access
    5. **API Access** - Mobile app uses Sanctum token for API calls

    ---

    Session Management



    API Token Management

    Users can manage their active sessions and API tokens:

    // List user's active tokens
    GET /api/tokens

    // Revoke specific token
    DELETE /api/tokens/{tokenId}

    // Revoke all tokens (except current)
    DELETE /api/tokens

    // Logout (revoke current token)
    POST /api/logout


    Session Information



    // Token details
    $tokenInfo = [
    'id' => $token->id,
    'name' => $token->name,
    'abilities' => $token->abilities,
    'last_used_at' => $token->last_used_at,
    'created_at' => $token->created_at,
    'ip_address' => $token->last_used_ip ?? 'Unknown',
    'user_agent' => $this->parseUserAgent($token->user_agent),
    ];

    ---

    External App Authorization



    OAuth for External Apps

    danwel can act as an OAuth provider for external applications:

    // OAuth authorization endpoint
    Route::get('/oauth/authorize', [ExternalAppAuthController::class, 'authorize']);
    Route::post('/oauth/authorize', [ExternalAppAuthController::class, 'approve']);

    // Authorization flow
    public function authorize(Request $request)
    {
    // Validate client_id, redirect_uri, scope
    $client = OAuthClient::where('client_id', $request->client_id)->first();

    if (!$client || !$this->isValidRedirectUri($request->redirect_uri, $client)) {
    abort(400, 'Invalid client or redirect URI');
    }

    // Show authorization prompt
    return view('auth.external-app-authorize', compact('client', 'request'));
    }


    Scope-Based Permissions

    External apps request specific scopes:

    | Scope | Description | Access Level |
    |-------|-------------|-------------|
    | read:user | Read user profile | Basic user info |
    | read:time-blocks | Read time blocks | Time tracking data |
    | write:time-blocks | Create/edit time blocks | Time tracking write |
    | read:projects | Read projects/clients | Project data |

    ---

    Security Features



    Password Security



  • • **Bcrypt Hashing** - Strong password hashing with cost factor 12

  • • **Password Requirements** - Minimum length, complexity rules

  • • **Breach Detection** - Check against known breached passwords

  • • **Reset Security** - Secure password reset with token expiration


  • Rate Limiting



    // Authentication rate limits
    'login' => 'throttle:5,1', // 5 attempts per minute
    'magic-link' => 'throttle:3,1', // 3 magic links per minute
    'mobile-token' => 'throttle:5,1', // 5 mobile tokens per minute
    'api' => 'throttle:api', // API rate limiting


    Session Security



  • • **Secure Cookies** - HttpOnly, Secure, SameSite

  • • **Session Regeneration** - New session ID after login

  • • **CSRF Protection** - All state-changing requests protected

  • • **IP Validation** - Optional IP address validation


  • Account Security



    // Security event notifications
    class SecurityEventListener
    {
    public function handle($event)
    {
    switch ($event->type) {
    case 'password_changed':
    Mail::send(new PasswordChangedEmail($event->user));
    break;
    case '2fa_enabled':
    Mail::send(new TwoFactorEnabledEmail($event->user));
    break;
    case '2fa_disabled':
    Mail::send(new TwoFactorDisabledEmail($event->user));
    break;
    }
    }
    }

    ---

    Usage Examples



    User Registration with OAuth



    ``javascript
    // Frontend OAuth initiation
    function initiateOAuth(provider) {
    window.location.href =
    /auth/${provider};
    }

    // Handle OAuth return
    // User is automatically logged in after successful OAuth callback

    Adding Passkey


    javascript
    // Register new passkey
    async function registerPasskey() {
    try {
    // Get registration options from server
    const optionsResponse = await fetch('/webauthn/register/options');
    const options = await optionsResponse.json();

    // Create credential
    const credential = await navigator.credentials.create({
    publicKey: options.publicKey
    });

    // Send credential to server
    await fetch('/webauthn/register', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
    credential: {
    id: credential.id,
    rawId: Array.from(new Uint8Array(credential.rawId)),
    response: {
    clientDataJSON: Array.from(new Uint8Array(credential.response.clientDataJSON)),
    attestationObject: Array.from(new Uint8Array(credential.response.attestationObject))
    },
    type: credential.type
    }
    })
    });

    alert('Passkey registered successfully!');
    } catch (error) {
    console.error('Passkey registration failed:', error);
    }
    }
    ``

    Enabling 2FA



    // Enable TOTP 2FA
    Route::post('/user/two-factor-authentication', function (Request $request) {
    $request->user()->enableTwoFactorAuthentication();

    return back()->with('status', 'two-factor-authentication-enabled');
    });

    // Confirm 2FA setup
    Route::post('/user/confirmed-two-factor-authentication', function (Request $request) {
    $confirmed = $request->user()->confirmTwoFactorAuthentication($request->code);

    if ($confirmed) {
    event(new TwoFactorAuthenticationEnabled($request->user()));
    return back()->with('status', 'two-factor-authentication-confirmed');
    }

    return back()->withErrors(['code' => 'Invalid authentication code']);
    });

    ---

    This advanced authentication system provides users with flexible, secure login options while maintaining strong security standards across all authentication methods.