hughbertd/oauth2-unsplash
OAuth2 client provider for Unsplash built on league/oauth2-client. Install via Composer and use the Unsplash provider to run the Authorization Code flow, fetch access tokens, and retrieve the authenticated user (resource owner) for API access.
Install the package:
composer require hughbertd/oauth2-unsplash
Add Unsplash credentials to .env:
UNSPLASH_CLIENT_ID=your_client_id
UNSPLASH_CLIENT_SECRET=your_client_secret
UNSPLASH_REDIRECT_URI=http://your-app.test/callback
Create a service class (e.g., app/Services/UnsplashService.php):
namespace App\Services;
use HughbertD\OAuth2\Client\Provider\Unsplash;
use Illuminate\Support\Facades\Session;
class UnsplashService
{
public function getProvider(): Unsplash
{
return new Unsplash([
'clientId' => config('services.unsplash.client_id'),
'clientSecret' => config('services.unsplash.client_secret'),
'redirectUri' => config('services.unsplash.redirect_uri'),
]);
}
public function getAuthorizationUrl()
{
$provider = $this->getProvider();
Session::put('oauth2state', $provider->getState());
return $provider->getAuthorizationUrl();
}
public function getAccessToken(string $code)
{
$provider = $this->getProvider();
return $provider->getAccessToken('authorization_code', ['code' => $code]);
}
public function getUser(array $token)
{
$provider = $this->getProvider();
return $provider->getResourceOwner($token);
}
}
Add routes in routes/web.php:
Route::get('/auth/unsplash', [AuthController::class, 'redirectToUnsplash'])->name('unsplash.auth');
Route::get('/auth/unsplash/callback', [AuthController::class, 'handleUnsplashCallback']);
First use case: Redirect to Unsplash for auth
// AuthController.php
public function redirectToUnsplash()
{
return redirect()->to(app(UnsplashService::class)->getAuthorizationUrl());
}
public function handleUnsplashCallback()
{
try {
$token = app(UnsplashService::class)->getAccessToken(request('code'));
$user = app(UnsplashService::class)->getUser($token->toArray());
// Store token/user in session or database
auth()->loginUsingId($user->getId(), true); // Example: Use Unsplash user ID
return redirect()->route('dashboard');
} catch (Exception $e) {
return redirect()->route('home')->with('error', $e->getMessage());
}
}
Initiate Auth:
getAuthorizationUrl().oauth2state).Callback Handling:
state matches session to prevent CSRF.code for access_token using getAccessToken().API Interaction:
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.unsplash.com/me', [
'headers' => ['Authorization' => 'Bearer ' . $token['access_token']],
]);
Service Container Binding:
Bind the service class in AppServiceProvider:
public function register()
{
$this->app->singleton(UnsplashService::class, function ($app) {
return new UnsplashService();
});
}
Token Persistence:
Store tokens in the database using a model (e.g., UnsplashToken):
// UnsplashService.php
public function storeToken(array $token)
{
return UnsplashToken::updateOrCreate(
['user_id' => auth()->id()],
['token' => $token['access_token'], 'refresh_token' => $token['refresh_token'] ?? null]
);
}
Middleware for Protected Routes:
// app/Http/Middleware/EnsureUnsplashToken.php
public function handle($request, Closure $next)
{
if (!auth()->user()->unsplashToken) {
return redirect()->route('unsplash.auth');
}
return $next($request);
}
Event-Based Workflows: Dispatch events for token refresh or user sync:
// After fetching user data
event(new UnsplashUserSynced($user));
Combine with Guzzle: Use Guzzle for API calls with the token:
$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.unsplash.com']);
$response = $client->request('GET', '/photos', [
'auth' => [$token['access_token'], '']
]);
Laravel Socialite Alternative: If using Laravel Socialite, register the provider:
Socialite::extend('unsplash', function ($app) {
$config = $app['config']['services.unsplash'];
return Socialite::buildProvider(
HughbertD\OAuth2\Client\Provider\Unsplash::class,
$config
);
});
Caching Responses: Cache Unsplash API responses (e.g., photos) using Laravel’s cache:
$photos = Cache::remember('unsplash_photos', now()->addHours(1), function () {
return $this->unsplashService->fetchPhotos();
});
Stale Package:
Missing Token Refresh:
refreshToken() method or use Unsplash’s /token endpoint manually:
$provider->getAccessToken('refresh_token', ['refresh_token' => $refreshToken]);
CSRF/State Validation:
csrf_token() middleware must be used.state:
public function handle($request, Closure $next)
{
if ($request->session()->get('oauth2state') !== $request->query('state')) {
throw new \Exception('CSRF state validation failed');
}
return $next($request);
}
No Laravel Facades:
Auth, Cache).Unsplash API Changes:
/authorize URL, scopes).Error Handling:
try {
$token = $provider->getAccessToken('authorization_code', ['code' => $code]);
} catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
if (strpos($e->getMessage(), 'invalid_grant') !== false) {
// Handle expired code/token
}
}
Enable Debug Mode: Configure the provider for verbose logs:
$provider = new Unsplash([
'clientId' => $clientId,
'clientSecret' => $clientSecret,
'redirectUri' => $redirectUri,
'debug' => true, // Enable debug mode
]);
Inspect Raw Responses:
Use getLastResponse() to debug API calls:
try {
$token = $provider->getAccessToken('authorization_code', ['code' => $code]);
} catch (Exception $e) {
dd($provider->getLastResponse()->getBody());
}
Test with Postman: Manually test Unsplash’s OAuth2 flow using Postman to isolate issues:
GET https://unsplash.com/oauth/authorize?client_id=...&redirect_uri=...&response_type=codePOST https://unsplash.com/oauth/token with code and credentials.How can I help you explore Laravel packages today?