Add integration guidance that provider abuse controls can apply across related auth endpoints based on source identity, reinforcing backoff and retry discipline.
155 lines
5.1 KiB
Markdown
155 lines
5.1 KiB
Markdown
# Integration Guide (British English)
|
|
|
|
This guide shows a complete OAuth/OIDC login flow using `BlackWall\Auth\AuthClient`.
|
|
|
|
## 1. Start login
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
session_start();
|
|
require __DIR__ . '/vendor/autoload.php';
|
|
|
|
use BlackWall\Auth\AuthClient;
|
|
use BlackWall\Auth\Config;
|
|
|
|
$client = new AuthClient(Config::fromArray([
|
|
'clientId' => 'your-client-id',
|
|
'authorizeUrl' => 'https://blackwall.cx/oauth/authorize',
|
|
'tokenUrl' => 'https://blackwall.cx/oauth/token',
|
|
'userInfoUrl' => 'https://blackwall.cx/oauth/userinfo',
|
|
'redirectUri' => 'https://your-app.example/callback.php',
|
|
'scope' => 'openid profile email offline_access',
|
|
]));
|
|
|
|
if (!isset($_SESSION['user'])) {
|
|
$auth = $client->buildAuthorisationUrl([
|
|
'extra' => [
|
|
// Some providers require nonce for OIDC requests.
|
|
'nonce' => rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '='),
|
|
],
|
|
]);
|
|
// Prevent stale redirects from being reused from cache.
|
|
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
|
header('Pragma: no-cache');
|
|
header('Location: ' . $auth['url']);
|
|
exit;
|
|
}
|
|
|
|
echo 'Already signed in.';
|
|
```
|
|
|
|
## 2. Handle callback
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
session_start();
|
|
require __DIR__ . '/vendor/autoload.php';
|
|
|
|
use BlackWall\Auth\AuthClient;
|
|
use BlackWall\Auth\Config;
|
|
|
|
$client = new AuthClient(Config::fromArray([
|
|
'clientId' => 'your-client-id',
|
|
'authorizeUrl' => 'https://blackwall.cx/oauth/authorize',
|
|
'tokenUrl' => 'https://blackwall.cx/oauth/token',
|
|
'userInfoUrl' => 'https://blackwall.cx/oauth/userinfo',
|
|
'redirectUri' => 'https://your-app.example/callback.php',
|
|
]));
|
|
|
|
if (isset($_GET['error'])) {
|
|
$error = (string) $_GET['error'];
|
|
$description = isset($_GET['error_description']) ? (string) $_GET['error_description'] : 'Unknown provider error';
|
|
// access_denied is a normal authorisation outcome (for example, membership
|
|
// revoked, privilege changed, or user denied consent between request and submit).
|
|
http_response_code($error === 'access_denied' ? 403 : 400);
|
|
exit('Authorization failed: ' . $description);
|
|
}
|
|
|
|
$result = $client->handleCallback($_GET);
|
|
|
|
$_SESSION['user'] = [
|
|
'email' => $result->user->email,
|
|
'privilege_level' => $result->user->privilegeLevel,
|
|
'role' => $result->user->role,
|
|
];
|
|
$_SESSION['access_token'] = $result->tokens->accessToken;
|
|
$_SESSION['refresh_token'] = $result->tokens->refreshToken;
|
|
|
|
header('Location: /');
|
|
exit;
|
|
```
|
|
|
|
## 3. Single callback for multiple app roles
|
|
|
|
Using one OAuth client and one callback URL is recommended.
|
|
|
|
Map users in your application by:
|
|
|
|
1. `email` claim to local account record
|
|
2. `privilege_level` (or `role`) to application role
|
|
|
|
Typical mapping example:
|
|
|
|
- `privilege_level = 1` => admin/superadmin
|
|
- `privilege_level = 2` => user/tutor
|
|
|
|
If `privilege_level` is absent, the SDK also resolves common role strings (`admin`, `superadmin`, `user`, `tutor`).
|
|
|
|
## 4. Refresh token (optional)
|
|
|
|
```php
|
|
<?php
|
|
|
|
$tokens = $client->refreshAccessToken($_SESSION['refresh_token']);
|
|
$_SESSION['access_token'] = $tokens->accessToken;
|
|
if ($tokens->refreshToken !== null) {
|
|
$_SESSION['refresh_token'] = $tokens->refreshToken;
|
|
}
|
|
```
|
|
|
|
## Exceptions
|
|
|
|
Catch specific exceptions for clearer handling:
|
|
|
|
- `BlackWall\Auth\Exception\StateMismatchException`
|
|
- `BlackWall\Auth\Exception\TokenExchangeException`
|
|
- `BlackWall\Auth\Exception\UserInfoException`
|
|
- `BlackWall\Auth\Exception\TransportException`
|
|
|
|
Example:
|
|
|
|
```php
|
|
try {
|
|
$tokens = $client->exchangeCodeForTokens($code);
|
|
} catch (\BlackWall\Auth\Exception\TokenExchangeException $e) {
|
|
error_log('Token exchange failed: ' . $e->getMessage());
|
|
error_log('Auth code: ' . ($e->authCode() ?? 'none'));
|
|
http_response_code(502);
|
|
}
|
|
```
|
|
|
|
`UserInfoException` with `invalid_token` can occur when provider-side subject scope is no longer active (for example user/project disabled) between token issuance and userinfo retrieval.
|
|
|
|
## Operational guidance
|
|
|
|
- Keep provider URLs in environment variables, not hard-coded.
|
|
- Avoid printing tokens in production pages.
|
|
- Expect `429 Too Many Requests` from provider control endpoints under abuse protection; implement backoff/retry instead of tight loops.
|
|
- Apply the same backoff strategy for provider `userinfo` calls that return `429 Too Many Requests`.
|
|
- For token exchange and refresh, apply bounded retry/backoff on transient provider failures instead of immediate repeated retries.
|
|
- If you call provider WebAuthn login challenge/verify endpoints directly, use `POST` only.
|
|
- Configure OAuth client redirect URIs as HTTPS in production; reserve HTTP for localhost loopback testing only.
|
|
- Assume provider rate limits are enforced per source identity (for example IP) and shared across related auth endpoints.
|
|
- Rotate client secrets for confidential clients.
|
|
- Add a unique `nonce` to each OIDC authorisation request.
|
|
- Validate `state` on every callback request.
|
|
- Use secure session cookies (`Secure`, `HttpOnly`, `SameSite=Lax` or stricter).
|
|
- HTTPS URLs are enforced by default in `Config::fromArray()`.
|
|
- For localhost-only HTTP testing, explicitly set `allowInsecureHttp => true`.
|