STEP2-6. ソーシャルログイン機能を作ってみよう
アプリケーション側で独自のIDを保有し、アプリ固有の情報をIDと紐付けます。
IDの認証には外部のサービスが提供している認証基盤を利用して認証処理を実装します。
実装に必要な手順
socialite のインストール
公式の言われるがままに作成した雛形のプロジェクトへインストールを行います
composer require laravel/socialite
guzzle のインストール
公式の言われるがままに作成した雛形のプロジェクトへインストールを行います
composer require guzzlehttp/guzzle
アプリ登録
公式の言われるがままにアプリ登録を行います
それぞれ入力項目には以下の内容を入力してください
- Application name:自由に入力してください
- Homepage URL:自分が起動してるlaravelのアドレス(例:http://localhost)
- Application description:アプリに詳細
- Authorization callback URL:自分が起動してるlaravelのアドレスに/login/github/callbackを追加したもの (例:http://localhost/login/github/callback)
実装 - 認証・認可
- ログインしたユーザのリポジトリ一覧を表示する
- ログインしたユーザとしてissueを発行する
上記なことが行えるようなアプリケーションを実装します。
追加・修正を行ったファイルを下記に記します。
1-1. 実装(設定値)
.env
を修正
file_path:projectname/src/.env
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:jzP79v5XJFc2spSWU6iQsvDioZpCNUNK4g6YtieCk/Y=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
GITHUB_CLIENT_ID={`アプリ登録` で取得した値を利用してください} # 追加!
GITHUB_CLIENT_SECRET={`アプリ登録` で取得した値を利用してください} # 追加!
config/services.php
を修正
file_path:projectname/src/config/services.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Third Party Services
|--------------------------------------------------------------------------
|
| This file is for storing the credentials for third party services such
| as Stripe, Mailgun, SparkPost and others. This file provides a sane
| default location for this type of information, allowing packages
| to have a conventional place to find your various credentials.
|
*/
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'), // 追加!
'client_secret' => env('GITHUB_CLIENT_SECRET'), // 追加!
'redirect' => '/login/github/callback',
],
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
],
'ses' => [
'key' => env('SES_KEY'),
'secret' => env('SES_SECRET'),
'region' => env('SES_REGION', 'us-east-1'),
],
'sparkpost' => [
'secret' => env('SPARKPOST_SECRET'),
],
'stripe' => [
'model' => App\User::class,
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
'webhook' => [
'secret' => env('STRIPE_WEBHOOK_SECRET'),
'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300),
],
],
];
1-2. 実装(ルーティング)
routes/web.php
を修正(下記4行を追加)
file_path:projectname/src/routes/web.php
Route::get('github', 'Github\GithubController@top');
Route::post('github/issue', 'Github\GithubController@createIssue');
Route::get('login/github', 'Auth\LoginController@redirectToProvider');
Route::get('login/github/callback', 'Auth\LoginController@handleProviderCallback');
1-3. 実装(コントローラ)
app/Http/Controllers/Auth/LoginController.php
を修正
file_path:projectname/src/app/Http/Controllers/Auth/LoginController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Socialite;// 追加!
use Illuminate\Http\Request;// 追加!
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
/**
* GitHubの認証ページヘユーザーをリダイレクト
*
* @return \Illuminate\Http\Response
*/
public function redirectToProvider()// 追加!
{
return Socialite::driver('github')->scopes(['read:user', 'public_repo'])->redirect();
}
/**
* GitHubからユーザー情報を取得
*
* @return \Illuminate\Http\Response
*/
public function handleProviderCallback(Request $request)// 追加!
{
$user = Socialite::driver('github')->user();
$request->session()->put('github_token', $user->token);
return redirect('github');
}
}
example-auth/app/Http/Controllers/Github/GithubController.php
を追加
file_path:projectname/src/app/Http/Controllers/Github/GithubController.php
<?php
namespace App\Http\Controllers\Github;
use App\Http\Controllers\Controller;
use Socialite;
use Illuminate\Http\Request;
class GithubController extends Controller
{
public function top(Request $request)
{
$token = $request->session()->get('github_token', null);
try {
$user = Socialite::driver('github')->userFromToken($token);
} catch (\Exception $e) {
return redirect('login/github');
}
$client = new \GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.github.com/user/repos', [
'auth' => [$user->user['login'], $token]
]);
return view('github', [
'info' => var_dump($user),
'nickname' => $user->nickname,
'token' => $token,
'repos' => array_map(function($o) {
return $o->name;
}, json_decode($res->getBody()))
]);
}
public function createIssue(Request $request)
{
$token = $request->session()->get('github_token', null);
$user = Socialite::driver('github')->userFromToken($token);
$client = new \GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.github.com/repos/' . $user->user['login'] . '/' . $request->input('repo') . '/issues', [
'auth' => [$user->user['login'], $token],
'json' => [
'title' => $request->input('title'),
'body' => $request->input('body')
]
]);
return view('done', [
'response' => json_decode($res->getBody())->html_url
]);
}
}
1-4. 実装(ビュー)
resources/views/done.blade.php
を追加
file_path:projectname/src/resources/views/done.blade.php
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>github</title>
</head>
<body>
{{ $response }}
つくったよ
</body>
</html>
resources/views/github.blade.php
を追加
file_path:projectname/src/resources/views/github.blade.php
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>github</title>
</head>
<body>
<div>{{ $info }}</div>
<div>あなたのニックネームは{{ $nickname }}です</div>
<div>あなたのトークンは{{ $token }}です</div>
<div>リポジトリ一覧</div>
<ul>
@foreach($repos as $repo)
<li>{{ $repo }}</li>
@endforeach
</ul>
<form action="/github/issue" method="post">
{{ csrf_field() }}
<div>repo : <input type="text" name="repo"></div>
<div>title : <input type="text" name="title"></div>
<div>body : <input type="text" name="body"></div>
<input type="submit" value="Confirm">
</form>
</body>
</html>
実行結果
/github
へ直接アクセスすると未だ認証・認可されてないので login/github
へリダイレクトされます。
その後取得された token を持って認可された操作が行えるようになります。
ほとんど伏せてますが
- ユーザ情報
- 認証したユーザのリポジトリ一覧
が表示されているはずです。
また、フォームに リポジトリ一覧から任意のリポジトリをコピペし、title、 body を入力すれば指定したリポジトリに issue が作られることが確認できます。
トークン取得までの流れ
トークンが取得され次第 test_login アプリケーションが user の権限を以って github.com へリソースアクセスを行います
実装 - ログイン機能
認証・認可の仕組みを理解できたところで、ログイン機能を実装します。 具体的な内容は下記となります。
- ログインフォームがある
- ログイン後、IDと紐づくような情報の確認、編集ができる
尚、実装内容は 認証・認可 が実装完了してることを前提としています。
1-1. DBテーブルのセットアップ
artisan を利用したマイグレーションを利用します 下記コマンドを workspace コンテナ上で実行してマイグレーションを生成します
php artisan make:migration create-table
ファイルは下記のパスに生成されます。
database/migrations/2018_12_03_101836_create-table.php
そして、内容を下記のように修正します。
file_path:projectname/src/database/migrations/2018_12_03_101836_create-table.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTable extends Migration
{
/**
* マイグレーション実行
*
* @return void
*/
public function up()
{
Schema::create('user', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->nullable();
$table->string('comment')->nullable();
$table->string('github_id');
$table->timestamps();
});
}
/**
* マイグレーションを元に戻す
*
* @return void
*/
public function down()
{
Schema::drop('user');
}
}
artisan コマンドでマイグレーションを行います
workspace コンテナ側でプロジェクト直下の .env
ファイルの db に関する設定を変更します
.env
file_path:projectname/src/.env
DB_CONNECTION=pgsql
DB_HOST=laradock_postgres_1
DB_PORT=5432
DB_DATABASE=default
DB_USERNAME=default
DB_PASSWORD=secret
設定が完了しました。workspace コンテナ側でマイグレーションを行います
php artisan migrate
下記のような出力が表示されたら成功です。
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrating: 2018_12_03_101836_create-table
Migrated: 2018_12_03_101836_create-table
先ほどの.envで設定したものを元にDBへログインします。
psql -h laradock_postgres_1 -U default -d default
各オプションの詳細について知りたい方はこちらをみてください
本当にテーブルが作成されていたか確認してみます。
default-# \d user;
Table "public.user"
Column | Type | Modifiers
------------+--------------------------------+---------------------------------------------------
id | integer | not null default nextval('user_id_seq'::regclass)
name | character varying(255) |
comment | character varying(255) |
github_id | character varying(255) | not null
created_at | timestamp(0) without time zone |
updated_at | timestamp(0) without time zone |
Indexes:
"user_pkey" PRIMARY KEY, btree (id)
なんやら created_at と updated_at は自動で生成されるようです。
1-2. 実装(ルーティング)
routes/web.php
に下記1行を追加しました
file_path:projectname/src/routes/web.php
Route::post('user', 'User\UserController@updateUser');
1-3. 実装(コントローラ)
app/Http/Controllers/User/UserController.php
を追加
file_path:projectname/src/app/Http/Controllers/User/UserController.php
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Socialite;
use Illuminate\Support\Facades\DB;
class UserController extends Controller
{
public function updateUser(Request $request)
{
$token = $request->session()->get('github_token', null);
$user = Socialite::driver('github')->userFromToken($token);
DB::update('update public.user set name = ?, comment = ? where github_id = ?', [$request->input('name'), $request->input('comment'), $user->user['login']]);
return redirect('/github');
}
}
app/Http/Controllers/Auth/LoginController.php
を修正
handleProviderCallback 関数を下記のように修正
file_path:projectname/src/app/Http/Controllers/Auth/LoginController.php
public function handleProviderCallback(Request $request)
{
$github_user = Socialite::driver('github')->user();
$now = date("Y/m/d H:i:s");
$app_user = DB::select('select * from public.user where github_id = ?', [$github_user->user['login']]);
if (empty($app_user)) {
DB::insert('insert into public.user (github_id, created_at, updated_at) values (?, ?, ?)', [$github_user->user['login'], $now, $now]);
}
$request->session()->put('github_token', $github_user->token);
return redirect('github');
}
DBを利用するための記載を追加
use Illuminate\Support\Facades\DB;
app/Http/Controllers/Github/GithubController.php
を修正
top 関数を修正
file_path:projectname/src/app/Http/Controllers/Github/GithubController.php
public function top(Request $request)
{
$token = $request->session()->get('github_token', null);
try {
$github_user = Socialite::driver('github')->userFromToken($token);
} catch (\Exception $e) {
return redirect('login/github');
}
$client = new \GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.github.com/user/repos', [
'headers' => [
'Authorization' => 'token ' . $token
]
]);
$app_user = DB::select('select * from public.user where github_id = ?', [$github_user->user['login']]);
return view('github', [
'user' => $app_user[0],
'nickname' => $github_user->nickname,
'token' => $token,
'repos' => array_map(function($o) {
return $o->name;
}, json_decode($res->getBody()))
]);
}
DBを利用するための記載を追加
use Illuminate\Support\Facades\DB;
1-4. 実装(ビュー)
resources/views/github.blade.php
を修正
file_path:projectname/src/resources/views/github.blade.php
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>github</title>
</head>
<body>
<form action="/user" method="post">
{{ csrf_field() }}
<div>お名前 : <input type="text" name="name" value="{{$user->name}}"></div>
<div>コメント : <input type="text" name="comment" value="{{$user->comment}}"></div>
<input type="submit" value="Confirm">
</form>
<div>ようこそ{{ $nickname }}さん</div>
<div>あなたのトークンは{{ $token }}です</div>
<div>リポジトリ一覧</div>
<ul>
@foreach($repos as $repo)
<li>{{ $repo }}</li>
@endforeach
</ul>
<form action="/github/issue" method="post">
{{ csrf_field() }}
<div>repo : <input type="text" name="repo"></div>
<div>title : <input type="text" name="title"></div>
<div>body : <input type="text" name="body"></div>
<input type="submit" value="Confirm">
</form>
</body>
</html>
resources/views/welcome.blade.php
を修正
file_path:projectname/src/resources/views/welcome.blade.php
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<title>ログイン画面</title>
</head>
<body>
<a href="/login/github">githubアカウントでログイン</a>
</body>
</html>
動作確認
user テーブルが空の状態でログインを試みてみます
default=# select * from public.user;
id | name | comment | github_id | created_at | updated_at
----+------+---------+-----------+------------+------------
(0 rows)
http://localhost/
から リンクを押して /github
へ遷移します。
user テーブルに1つのレコードが追加されました
default=# select * from public.user;
id | name | comment | github_id | created_at | updated_at
----+------+---------+-----------+---------------------+---------------------
1 | | | hogefuga | 2018-12-21 07:32:24 | 2018-12-21 07:32:24
(1 row)
もう一度同じ手順を踏んでもレコードは増えません。
github.com の認証を経て test-login アプリ側のIDと github.com 側のIDが紐づいたと言えます。
お名前, コメント を入力の上 Confirm を押すとレコードの値が更新されていることがわかります。
default=# select * from public.user;
id | name | comment | github_id | created_at | updated_at
----+------+---------+-----------+---------------------+---------------------
1 | test | test | hogefuga | 2018-12-21 08:30:13 | 2018-12-21 08:30:13
(1 row)
まとめ
- 認証・認可の処理を github 側に任せることができた
- 作成したアプリ側のIDとgithub側のIDを紐づけることができた
最後まで読んでいただいてありがとうございました。