0

OAuth2 — an authorization framework, not an authentication protocol

Intermediate5 min read·eng-19-008
interviewsecurity

Concept

OAuth2 — an authorization framework (not authentication!) that allows third-party applications to access a user's resources on another service, without exposing the user's credentials.

The problem OAuth2 solves: You want your app to post tweets on behalf of users. Without OAuth, you'd ask for their Twitter password — terrible security. With OAuth2, Twitter shows the user a consent screen, and if approved, gives YOUR APP a token scoped to specific permissions ("post tweets"). Your app never sees their Twitter password.

Key roles:

  • Resource Owner: The user who owns the data.
  • Client: Your application requesting access.
  • Authorization Server: Issues tokens (e.g., Twitter's auth server).
  • Resource Server: The API with the protected resources (Twitter's API).

OAuth2 Grant Types:

  • Authorization Code (+ PKCE): For web apps and mobile. Most secure. User redirected to auth server, gets a code, code exchanged for token.
  • Client Credentials: Machine-to-machine. No user involved. Your server authenticates to another server.
  • Device Code: For TVs/CLIs with no browser.
  • Implicit (deprecated): Was for SPAs. Replaced by Authorization Code + PKCE.

OAuth2 vs OpenID Connect (OIDC): OAuth2 is for AUTHORIZATION (access to resources). OIDC adds an identity layer on top of OAuth2 (for AUTHENTICATION — "who is this user?"). "Login with Google" = OIDC.

In Laravel: laravel/socialite for OAuth2 login (Google, GitHub, etc.). laravel/passport for building your own OAuth2 server.

Code Example

php
<?php
// OAUTH2 CLIENT — Login with GitHub (using Laravel Socialite)
// composer require laravel/socialite

// config/services.php
// 'github' => [
//     'client_id'     => env('GITHUB_CLIENT_ID'),
//     'client_secret' => env('GITHUB_CLIENT_SECRET'),
//     'redirect'      => env('GITHUB_REDIRECT_URI'),
// ],

// routes/web.php
Route::get('/auth/github',          [SocialAuthController::class, 'redirect']);
Route::get('/auth/github/callback', [SocialAuthController::class, 'callback']);

class SocialAuthController extends Controller
{
    // Step 1: Redirect user to GitHub
    public function redirect(): RedirectResponse
    {
        return Socialite::driver('github')
            ->scopes(['read:user', 'user:email']) // request only needed permissions
            ->redirect();
        // User sees: "The Framework Architect wants to access your email address. Allow?"
    }

    // Step 2: GitHub redirects back with ?code=abc123
    // Socialite exchanges code for access token, fetches user info
    public function callback(): RedirectResponse
    {
        $githubUser = Socialite::driver('github')->user();

        $user = User::updateOrCreate(
            ['github_id' => $githubUser->getId()],
            [
                'name'       => $githubUser->getName(),
                'email'      => $githubUser->getEmail(),
                'avatar_url' => $githubUser->getAvatar(),
            ]
        );

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

// OAUTH2 SERVER — building your own (Laravel Passport)
// composer require laravel/passport
// php artisan passport:install

// Issuing tokens for your own API
$token = $user->createToken(
    name: 'mobile-app',
    scopes: ['read-orders', 'create-orders'] // scoped access
)->accessToken;

// Protecting routes
Route::middleware(['auth:api', 'scope:read-orders'])->group(function () {
    Route::get('/orders', [OrderController::class, 'index']);
});

// CLIENT CREDENTIALS — machine-to-machine
// Other services authenticate with client_id + client_secret
// No user involved