這裡,分別會用OAuth Server,以及 OAuth Client 兩個角度來說明 OAuth2 運作機制
一、建立 OAuth Server
安裝laravel
composer create-project --prefer-dist laravel/laravel laravel_oauth
cd laravel_oauth
設定資料庫
建立資料庫 ex. laravel_oauth 複製 .env.example 並命名為 .env
加入資料庫連線設定
生成 app key
產生 APP_KEY
php artisan key:generate
修改 session 儲存方式 (可選擇,如果要存在本地就可略過此步驟)
.evn
SESSION_DRIVER=database
建立 session migrate
php artisan session:table
重新 dump
composer dump-autoload
建立 migrate
php artisan migrate
建立 Authentication 架構
php artisan make:auth
建立 OAuth 2 passport
composer require laravel/passport
設定舊版本MySQL Migration
如果安裝的MySql版本較舊,可能會發生錯誤,因此需加入下方修改設定
app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Schema;
public function boot()
{
Schema::defaultStringLength(191);
}
Migrate
將 auth 及 passport 相關 migration 來自動建立資料表
php artisan migrate
生成加密鑰
透過 passport 安裝 encryption keys,這是在產生 access tokens 所需要的 keys 會產生 client 產生 access tokens 所需要的 “personal access” 及 “password grant”,
php artisan passport:install
USER 權限控制設定
安裝完畢後,開啟 app/User.php,並且加入 Laravel\Passport\HasApiTokens 輔助方法
可以讓你檢查使用者的 token 及作用域
app/User.php
<?php
namespace App;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
...
下一步,開啟 app/Providers/AuthServiceProvider.php 並且加入 use Laravel\Passport\Passport 以及在 boot 加入 Passport::routes(); 這個方法可以用來註冊/註銷 user 的 access tokens
app/Providers/AuthServiceProvider.php
<?php
//預設passport 不會過期,如果需要設定使用時間,可以開啟Carbon
//use Carbon\Carbon;
namespace App\Providers;
use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
Passport::routes();
//預設passport 不會過期,如果需要設定使用時間,可以開啟下方兩行
//Passport::tokensExpireIn(Carbon::now()->addDays(15));
//Passport::refreshTokensExpireIn(Carbon::now()->addDays(30));
}
}
最後,開啟 config/auth.php 將 api driver 改為 passport 當具有 authentication 的 API 請求時,可以調用 Passport 的 TokenGuard config/auth.php
<?php
...
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
// 'driver' => 'token',
'driver'=>'passport',
'provider' => 'users',
],
],
建立 Front-end 快速啟動 vue 元件
透過下方指令,快速安裝 Vue 元件
php artisan vendor:publish --tag=passport-components
加入 component
開啟 resources/assets/js/app.js line 19 ,加入下方 components 語法
Vue.component(
'passport-clients',
require('./components/passport/Clients.vue')
);
Vue.component(
'passport-authorized-clients',
require('./components/passport/AuthorizedClients.vue')
);
Vue.component(
'passport-personal-access-tokens',
require('./components/passport/PersonalAccessTokens.vue')
);
build assets
安裝 npm 以及透過 npm run dev 讓 Webpack 重新發布 assets 變更的項目至 public
如果安裝過程發生問題,請參考這篇,更新 node 相關模組
npm install
npm run dev
建立 OAuth clients 以及 Person access tokens
接下來準備建立相關的 Controller 及 Views 用來顯示 Front-end 快速啟動元件
這裡建立一個 SettingController
php artisan make:controller SettingsController
App\Http\Controllers\SettingController.php
<?php
...
class SettingsController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
return view('settings');
}
}
建立 View
resources\views\settings.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<passport-clients></passport-clients>
<passport-authorized-clients></passport-authorized-clients>
<passport-personal-access-tokens></passport-personal-access-tokens>
</div>
</div>
</div>
@endsection
增加 route
在 routes/web.php 加入新的 Controller
<?php
Route::get('/settings', 'SettingsController@index')->name('settings');
啟用 Server
php artisan serve --port 8000
查看 OAuth Server 介面
前往 settings 路徑
http://127.0.0.1:8000/settings
新增一個 Client user
在前面,我們在 SettingsController 有加入 $this->middleware(‘auth’); 因此,在第一次訪問 settings 頁面,要先創立一個帳號,創立完成後,預設會自動登入
接著前往 http://127.0.0.1:8000/settings
會看到以下畫面,還沒有任何的
我們先試著建立一個 USER,點選 Create New Client
輸入用戶名稱以及驗證後callback的網址
Name: test Redirect URL: http://127.0.0.1:8001/callback
可以在一個帳號登入的情況下,產生多組 client
這些 client id 都會和你的 userid 連結
OAuth Client
接下來我們要開始建立 OAuth Client
並且測試
這裡會透過 laravel + Guzzle 來建立一些HTTP來模擬與 OAuth Server 驗證
安裝 OAuth Client
首先,安裝 laravel client 端
composer create-project --prefer-dist laravel/laravel laravel_oauth_client
cd laravel_oauth_client
設定資料庫
建立資料庫 ex. laravel_oauth_client 複製 .env.example 並命名為 .env
加入資料庫連線設定
生成 app key
產生 APP_KEY
php artisan key:generate
安裝 Guzzle
Guzzle 是一個PHP 的HTTP client,可以透過他來產生HTTP請求
我們會透過這樣的方式,簡單的產生一些驗證請求
composer require guzzlehttp/guzzle
建立驗證機制
驗證機制的流程如圖所示:
大致可以分成三部分: 取得授權(Grant)、取得Token、取得機密資料
每一部分發送請求時,都需要在Header夾帶驗證所需要的參數,如下:
step 1.Authorization Request
- response_type: 授權類型
- client_id: 客戶端ID
- redirect_uri: 重新導向的URI
- scope: 授權範圍
- state: 任意值,將原封不動的返回
step 3.Authorization Grant
- grant_type: 表示授權類型,這裡使用固定值 authorization_code
- client_id: 客戶端ID
- client_secret: 客戶端 secret
- redirect_uri: 重新導向的URI
- code: Grant 授權碼
- state: 任意值,將原封不動的返回
step 5.Access Token
- Authorization: 須包含 token type(Bearer) 及 token 字串
上述驗證過程,在這裡都直接建構於 laravel_oauth_client 的 routers中:
routes/web.php
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
use Illuminate\Http\Request;
//Authorization Request and Get Authorization Grant
/*
在一開始,要先傳送 client id 至 OAuth Server 進行審核,
OAuth Server 會詢問你是否要同意這個 client id app 取得你的 USER 資料
當你確認授權之後,OAuth Server 就會提供 Authorization Grant 至 redirect_uri 路徑
*/
Route::get('/authorization_request', function () {
$query = http_build_query([
'client_id' => 3, // 輸入要測試的 Client ID
'redirect_uri' => 'http://127.0.0.1:8001/callback',////Get Authorization Grant
'response_type' => 'code',
'scope' => ''
]);
return redirect('http://127.0.0.1:8000/oauth/authorize?'.$query);
});
//Authorization Grant and Get Access Token
/*
取得 OAuth Server 提供的 Authorization Grant 之後,
此 client id app 要提供 Authorization Grant 以及
自己的 client id 及 secret number 至 OAuth Server 請求 token
當OAuth Server驗證 client id、secret number、Authorization Grant 皆通過之後,
就會提供 token 至 redirect_uri 路徑
*/
//Authorization Grant and Get Access Token
Route::get('/callback', function (Request $request) {
$response = (new GuzzleHttp\Client)->post('http://127.0.0.1:8000/oauth/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => 3, // 輸入要測試的 Client ID
'client_secret' => 'nhrq2zShxNDH57nGiXsJ4TZ7Q0KoGdF0BfsWAuCf', // 輸入 client secret
'redirect_uri' => 'http://127.0.0.1:8001/callback',//Get Access token
'code' => $request->code,
]
]);
session()->put('token', json_decode((string) $response->getBody(), true));
return redirect('/posts');
});
//Sent Access Token and get Protected Resource
/*
取得 Passport access token之後,想要在 auth server 開始取得 USER資料時,
都必須在 header 的 Authoriztion 都要設計成 Bearer token 格式
接下來,就只要拿著 access token 就能持續取得使用者相關資料
*/
Route::get('/posts', function () {
try {
$response = (new GuzzleHttp\Client)->get('http://127.0.0.1:8000/api/user',
[ //Sent Access Token
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.session()->get('token.access_token')
]
]);
return $response->getBody();
}catch(Exception $e){
return $e->getCode().$e->getMessage();
}
});
啟用 Client Server
php artisan serve --port 8001
執行
首先,前往 127.0.0.8001/authorization_request
Header會夾帶驗證所需的資料並且導向 OAuth server 127.0.0.1:8000/oauth/authorize
首次連結時,會要求先登入
登入完畢之後,就會顯示有一位 test 想要連結你的個人帳號,請點選Authorize,取得 Authorization Grant(授權)
接著就會自動來回往返驗證過程
直到client id app 取得最後的 access token ,就能拿這 access token 向 OAuth Server 取得資料
查看
當有 client 請求取得 passport 且取得 token 之後,可從 OAuth Server 查看目前有經過認證的 APP
http://127.0.0.1:8000/settings
應用
在 OAuth Server 可以透過 middleware(‘auth:api’) 來驗證
例如,我們建立一個 testsController
Route::get('/tests', 'testsController@index')->name('tests')->middleware('auth:api');
接著就能在 TestsController 裡來安全的返回使用者資料
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;//Authentication
class TestsController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
$user = Auth::user();
return 'hello this is Protected Resource, user name is '.$user->name;
}
}
要留意,設計這些API時,要避免機密資料外漏