dingo/oauth2-server
Dingo OAuth2 Server adds a full OAuth2 authorization server to Laravel and Dingo API, supporting grant types, scopes, access tokens, and middleware for securing API endpoints. Configurable and easy to integrate.
Treat this as a legacy integration, not a greenfield choice. The last release was April 2014, the repository is not clearly available, and the package predates modern OAuth guidance (PKCE, OAuth 2.1, OpenID Connect). Use it only when maintaining an existing Laravel 4/early Laravel 5 or Dingo API application, or when you need a small, self-contained OAuth 2.0 token endpoint and accept the maintenance risk.
Minimal steps:
Confirm the package still installs on your PHP version:
composer show dingo/oauth2-server
composer require dingo/oauth2-server
Check vendor/dingo/oauth2-server/composer.json for PHP constraints. On PHP 7.2+ you will likely need to patch deprecations or pin an older runtime.
Look first at these areas in vendor/dingo/oauth2-server/src:
Server/Authorization — issues tokens.Server/Resource — validates bearer tokens on protected routes.Grant/ — password, client credentials, authorization code, refresh token, implicit.Storage/ — persistence adapters and interfaces.Entity/ — token/client/scope value objects.Exception/ — OAuth error codes and responses.Pick one first use case: a password grant for a first-party SPA/mobile client, or client credentials for machine-to-machine access. Avoid authorization-code flows until you have verified redirect and state handling.
Bootstrap the authorization server manually if there is no framework bridge you trust:
use Dingo\OAuth2\Storage\Fluent;
use Dingo\OAuth2\Server\Authorization;
use Dingo\OAuth2\Grant\Password;
$storage = new Fluent($db); // existing PDO/Laravel connection
$server = new Authorization($storage);
$server->addGrant(new Password($storage));
// Route this behind POST /oauth/token
$server->issueAccessToken();
Exact constructor signatures changed across the 2013–2014 releases; confirm against the vendored source before copying examples from old blog posts.
Split authorization and resource servers. Use Server\Authorization only on the token endpoint (POST /oauth/token). Use Server\Resource (or the equivalent bearer-token validator) in middleware on protected API routes. Do not let the resource server issue tokens.
Register only the grants you need. Typical mappings:
| Client type | Grant |
|---|---|
| First-party mobile/SPA | password + refresh_token |
| Server-to-server | client_credentials |
| Third-party web app | authorization_code + refresh_token |
| Legacy JS app | implicit (avoid for new work) |
Register grants explicitly so unsupported grant_type values fail closed.
Use storage adapters as the integration seam. The package expects storage implementations for clients, access tokens, refresh tokens, authorization codes, and scopes. A common pattern is to extend or wrap the included Fluent adapter when using Laravel’s query builder, or implement the storage interfaces against your own schema. Keep a consistent schema:
oauth_clients (id, secret, name, redirect_uri, scopes)
oauth_access_tokens (token, client_id, user_id, scope, expires_at)
oauth_refresh_tokens(token, client_id, user_id, scope, expires_at)
oauth_codes (code, client_id, user_id, redirect_uri, scope, expires_at)
oauth_scopes (id, description)
Wrap token issuance behind a controller. Keep request parsing and response formatting in your application, and let the package handle grant validation and token creation:
public function token()
{
try {
return $this->oauthServer->issueAccessToken();
} catch (\Dingo\OAuth2\Exception\OAuthException $e) {
return response()->json([
'error' => $e->getError(),
'error_description' => $e->getMessage(),
], $e->getStatusCode());
}
}
Validate bearer tokens in middleware. Parse Authorization: Bearer <token>, reject missing/malformed headers with 401 and WWW-Authenticate: Bearer, then load the token and check expiry and scope. Attach the resolved user/client to the request for downstream authorization.
Scope checks belong in your app. The package can carry scopes, but route-level authorization (“does this token have orders:write?”) is usually application logic. Centralize it in middleware or a policy layer.
Refresh-token rotation. The package can issue refresh tokens, but reuse detection and rotation policy are generally your responsibility. On refresh, invalidate the old refresh token and issue a new pair. Log reuse attempts.
Testing pattern. Seed a client and user, call the token endpoint through an HTTP test, then use the returned bearer token against a protected route. For unit tests, mock the storage interfaces rather than hitting a real database.
Dingo API integration. If the surrounding app uses Dingo API, configure its oauth2 auth provider to point at this package’s resource server. Keep the Dingo API version and this package’s 2014 API aligned; they were designed for the same era and may not work with later Dingo releases.
league/oauth2-server, Laravel Passport, or a hosted identity provider. Do not expose this directly to third-party developers without a security review.vendor/dingo/oauth2-server/README.md and the source. Old tutorials frequently show method names that no longer match the final 2014 release.application/x-www-form-urlencoded. If your client sends JSON, grant_type, client_id, client_secret, and scope may not be read. Test with curl -d, not a JSON body.client_secret; do not issue one and pretend it is secret.expires_at comparisons use the same timezone and that expired tokens return invalid_grant/invalid_token appropriately. Add a small clock-skew allowance only if you understand the trade-off.error and error_description, and token endpoints should return 400/401 rather than generic 500 pages. Catch OAuthException at the boundary.Fluent adapter may assume specific table names. If your schema differs, either create compatibility views or implement a custom storage adapter.league/oauth2-server or Laravel Passport expecting them to cooperate. Pick one token implementation per application.null, client secret mismatch, expired/revoked token, scope mismatch, and request content type — in that order. Most “it just returns 401” issues are storage or content-type problems, not cryptographic ones.How can I help you explore Laravel packages today?