Files
oicd/docs/QUICKSTART.md
2025-11-30 00:07:24 +01:00

8.7 KiB

Quick Start Guide

Get your application integrated with this OIDC provider in 10 minutes.


For the Impatient

# 1. Get credentials from admin panel
https://your-idp.com/admin/login

# 2. Add to your app (Python example)
pip install authlib flask

# 3. Copy this code
# (see Python example below)

# 4. Done! Users can now log in via OIDC

Prerequisites

  • ✅ OIDC Provider deployed and accessible
  • ✅ Admin access to register your client
  • ✅ A web application with a backend (Node.js, Python, PHP, etc.)

Step 1: Register Your Application (2 minutes)

Via Admin Panel

  1. Navigate to: https://your-idp.com/admin/login
  2. Login with admin credentials
  3. Go to "Clients" → "Create New Client"
  4. Fill in:
    • Client Name: My App
    • Redirect URIs: http://localhost:3000/callback (one per line)
    • Allowed Scopes: openid, profile, email
  5. Click "Create"
  6. Copy your credentials:
    Client ID: abc123def456
    Client Secret: xyz789... (⚠️ save this - shown only once!)
    

Step 2: Choose Your Integration Method (1 minute)

Pick the method that matches your tech stack:

If you use... Go to
Python + Flask Python Example
Node.js + Express Node.js Example
PHP + Laravel PHP Example
Any other Generic HTTP Flow

Python (Flask)

Install Dependencies

pip install flask authlib requests

Code (app.py)

from flask import Flask, redirect, url_for, session, jsonify
from authlib.integrations.flask_client import OAuth
import os

app = Flask(__name__)
app.secret_key = os.urandom(24)

# Configure OIDC
oauth = OAuth(app)
oauth.register(
    name='myidp',
    client_id='YOUR_CLIENT_ID',
    client_secret='YOUR_CLIENT_SECRET',
    server_metadata_url='https://your-idp.com/.well-known/openid-configuration',
    client_kwargs={'scope': 'openid profile email'}
)

@app.route('/')
def index():
    user = session.get('user')
    if user:
        return jsonify(user)
    return '<a href="/login">Login with OIDC</a>'

@app.route('/login')
def login():
    redirect_uri = url_for('callback', _external=True)
    return oauth.myidp.authorize_redirect(redirect_uri)

@app.route('/callback')
def callback():
    token = oauth.myidp.authorize_access_token()
    session['user'] = token['userinfo']
    return redirect('/')

@app.route('/logout')
def logout():
    session.pop('user', None)
    return redirect('/')

if __name__ == '__main__':
    app.run(port=3000, debug=True)

Run

python app.py
# Open http://localhost:3000

Node.js (Express)

Install Dependencies

npm install express express-session passport openid-client

Code (server.js)

const express = require('express');
const session = require('express-session');
const passport = require('passport');
const { Issuer, Strategy } = require('openid-client');

const app = express();

app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());

passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));

// Discover and configure OIDC
Issuer.discover('https://your-idp.com/.well-known/openid-configuration')
  .then(issuer => {
    const client = new issuer.Client({
      client_id: 'YOUR_CLIENT_ID',
      client_secret: 'YOUR_CLIENT_SECRET',
      redirect_uris: ['http://localhost:3000/callback'],
      response_types: ['code'],
    });

    passport.use('oidc', new Strategy({ client }, (tokenSet, userinfo, done) => {
      return done(null, userinfo);
    }));

    // Routes
    app.get('/', (req, res) => {
      if (req.isAuthenticated()) {
        res.send(`<h1>Hello, ${req.user.name}!</h1><a href="/logout">Logout</a>`);
      } else {
        res.send('<a href="/login">Login with OIDC</a>');
      }
    });

    app.get('/login', passport.authenticate('oidc'));

    app.get('/callback',
      passport.authenticate('oidc', { failureRedirect: '/' }),
      (req, res) => res.redirect('/')
    );

    app.get('/logout', (req, res) => {
      req.logout(() => res.redirect('/'));
    });

    app.listen(3000, () => console.log('App running on http://localhost:3000'));
  });

Run

node server.js
# Open http://localhost:3000

PHP (Laravel)

Install Socialite

composer require laravel/socialite
composer require socialiteproviders/oidc

Configure (config/services.php)

'oidc' => [
    'client_id' => env('OIDC_CLIENT_ID'),
    'client_secret' => env('OIDC_CLIENT_SECRET'),
    'redirect' => env('OIDC_REDIRECT_URI'),
    'base_url' => env('OIDC_ISSUER'),
],

Environment (.env)

OIDC_CLIENT_ID=YOUR_CLIENT_ID
OIDC_CLIENT_SECRET=YOUR_CLIENT_SECRET
OIDC_REDIRECT_URI=http://localhost:8000/callback
OIDC_ISSUER=https://your-idp.com

Routes (routes/web.php)

use Laravel\Socialite\Facades\Socialite;
use Illuminate\Support\Facades\Auth;

Route::get('/login', function () {
    return Socialite::driver('oidc')->redirect();
});

Route::get('/callback', function () {
    $user = Socialite::driver('oidc')->user();

    // Find or create user in database
    $localUser = User::updateOrCreate(
        ['email' => $user->email],
        ['name' => $user->name]
    );

    Auth::login($localUser);

    return redirect('/dashboard');
});

Route::get('/logout', function () {
    Auth::logout();
    return redirect('/');
});

Run

php artisan serve
# Open http://localhost:8000

Generic HTTP Flow

If you can't use a library, here's the manual flow:

Step 1: Redirect to Authorization Endpoint

GET https://your-idp.com/authorize?
  client_id=YOUR_CLIENT_ID&
  redirect_uri=http://localhost:3000/callback&
  response_type=code&
  scope=openid%20profile%20email&
  state=RANDOM_STATE_TOKEN

Step 2: Handle Callback

User is redirected back with a code:

http://localhost:3000/callback?code=ABC123&state=RANDOM_STATE_TOKEN

Verify state token to prevent CSRF!

Step 3: Exchange Code for Token

curl -X POST https://your-idp.com/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=ABC123" \
  -d "redirect_uri=http://localhost:3000/callback" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

Response:

{
  "access_token": "eyJhbGci...",
  "id_token": "eyJhbGci...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Step 4: Get User Info

curl https://your-idp.com/userinfo \
  -H "Authorization: Bearer ACCESS_TOKEN"

Response:

{
  "sub": "user-123",
  "email": "john@example.com",
  "name": "John Doe",
  "preferred_username": "john"
}

Testing Your Integration

1. Start Your App

# Your app should now be running on localhost

2. Click "Login"

Navigate to your app's login link. You should be redirected to:

https://your-idp.com/authorize?client_id=...

3. Login

Use test credentials:

Username: test
Password: test123

4. Verify

After login, you should:

  • ✅ Be redirected back to your app
  • ✅ See user information
  • ✅ Have an active session

Common Issues

"Invalid Redirect URI"

Problem: Redirect URI doesn't match.

Fix:

  1. Check exact match (including trailing slash)
  2. Update in admin panel if needed

"Invalid Client"

Problem: Wrong client ID or secret.

Fix:

  1. Double-check credentials
  2. No extra spaces or line breaks

"Connection Refused"

Problem: OIDC provider not accessible.

Fix:

  1. Verify provider is running: curl https://your-idp.com/health
  2. Check network/firewall

CORS Errors (for SPAs)

Problem: Browser blocks cross-origin requests.

Solution: Don't call OIDC endpoints from frontend. Use a backend proxy.


Next Steps

Once basic login works:

  1. Add User Persistence: Store user in your database
  2. Handle Logout: Clear session and optionally redirect to IdP logout
  3. Refresh Tokens: Implement token refresh (when available)
  4. Error Handling: Add proper error pages
  5. Production Setup: Use HTTPS, secure cookies

Complete Examples

Check out complete example applications:

  • Python Flask: examples/python-flask/ (coming soon)
  • Node.js Express: examples/nodejs-express/ (coming soon)
  • PHP Laravel: examples/php-laravel/ (coming soon)

Need Help?


You're all set! 🎉

Your users can now log in via OIDC in just a few clicks.