こんにちは!
バックエンドエンジニアからフルスタックエンジニアにメガ進化したいすぎちゃんです!
読者の皆さんはポートフォリオを作る中でこんな悩みに直面したことはないでしょうか?
「ポートフォリオサイトにログイン機能を実装してみたけど、何かイケてる感じが足りない...」
「ユーザーがログインするまでの手間を減らして、もっとスムーズに認証できないかな?」
せっかくポートフォリオを作るなら、もっとオシャレでスマートなログイン機能を搭載して、インパクトを与えたいですよね。
そこで今回は、AWSのCognitoを使いながら、LaravelでGoogle認証を組み合わせて、"カッコいい"認証機能を作る方法をご紹介します!
Googleアカウントを使えば、ユーザー名やパスワードを入力する必要がなくなり、ワンクリックでログイン完了。ユーザーのストレスも減り、操作感もグッと向上すること間違いありません!
成果物
こちらが今回の記事の成果物となります!
実際にGoogle認証はchatGPTやDropboxなどの大手のサイトで導入されています。
これまで普通のメールアドレス・パスワード形式でログイン画面を作っていた方も、ぜひ今回の内容を参考にして、ポートフォリオの印象を一気にランクアップさせてみてください!
事前準備
LaravelでGoogle認証を実装する前に、以下3の項目について事前に設定しておいてください
- Cognitoのユーザープール、ユーザー情報保管用のテーブルを用意
- Google認証をCognitoと連携
-
route/web.php
に認証関連のルーティングを追加
1. Cognitoのユーザープール、ユーザー情報保管用のテーブルを用意
2. Google認証をCognitoと連携
「ホストされたUIを編集」の画面で「許可されているコールバックURL」を以下のように設定しておきましょう。
http://<自身のアプリケーションのドメイン>/auth/google/callback
ローカルホストで開発する場合はhttp://localhost:8080/auth/google/callback
とします。
3. route/web.php
に認証関連のルーティングを追加
Route::get('/auth/google', "App\Http\Controllers\Auth\GoogleAuthController@redirectToGoogle")->name('auth.google');
Route::get('/auth/google/callback', "App\Http\Controllers\Auth\GoogleAuthController@handleOAuthCallback")->name('auth.google.callback');
-
/auth/google
Google認証ページにリダイレクトする処理を行います。 -
/auth/google/callback
Google認証後に呼び出されるコールバックURLです。
上記、3つの準備が完了したらLaravelでの実装に進んでいきましょう!
実装方法
Google認証によるログインを行なった場合、裏側では以下の流れで処理が行われます。
- 認証の要求を行う
- 受け取った認証コードを元にアクセストークンを要求する
- アクセストークンを元にユーザー情報を受け取る
- ユーザー情報をDBに登録
以下のコードを参考に実装を進めていきましょう
-
GoogleAuthController.php
GoogleのOAuth認証をトリガーし、Googleからの認証結果を処理するコントローラ。 -
GoogleAuthClient.php
Google API との通信を行うクライアントクラス。Googleの認証ページURLの生成、認証コードからトークンを取得する処理、トークンからユーザー情報を取得する処理を担当。 -
HttpClient.php
Google APIやCognitoなどの外部APIとのやり取りを抽象化するクライアント。 -
CreateGoogleUser.php
Googleユーザーをデータベースに保存するサービスクラスです。既存のユーザーがいれば取得し、なければ新規に作成します。
1. 認証の要求を行う
class GoogleAuthController extends Controller
{
# ... 省略
public function redirectToGoogle()
{
$url = $this->googleAuthClient->getAuthUrl();
return redirect($url);
}
「Googleアカウントでログイン」をタップすると/auth/google
を経由し、GoogleAuthControllerクラスのredirectToGoogle
メソッドが呼ばれます。
class GoogleAuthClient
{
#... 省略
public function getAuthUrl()
{
return $this->cognitoDomain.
"/oauth2/authorize".
"?identity_provider=Google".
"&redirect_uri=".$this->redirectUrl.
"&response_type=CODE".
"&client_id=".$this->appClientId.
"&scope=email openid";
}
次にGoogle認証の心臓部となるGoogleAuthClientクラスのgetAuthUrlが呼び出され、/oauth/authorize
にGETリクエストを送信します。
なお、GETリクエストのURLは以下の通りです。
https://<your-cognito-domain>/oauth2/authorize?identity_provider=Google&redirect_uri=REDIRECT_URL&response_type=CODE&client_id=YOUR_CLIENT_ID&scope=email openid
<your-cognito-domain>
の内容はユーザープール詳細画面の「アプリケーション統合」のタブをクリックすれば確認することができます。
なお、各パラーメータの説明についても念のため記載していきます。
パラメータ | 説明 |
---|---|
identity_provider | Cognitoの認証フローにおいて、指定された外部IDプロバイダー(Googleなど)を用いることを指定します。ここではGoogle と設定します。 |
redirect_uri | 認証が成功または失敗した後に、ユーザーをリダイレクトする先のURLです。ここではhttp://<自身のアプリケーションのドメイン>/auth/google/callback を設定します。 |
response_type | コールバックのクエリパラメータに何を返すかを指定します。ここではCODE と指定し、この認証コードを後でアクセストークンやIDトークンに交換するために使用します。
|
client_id | CognitoユーザープールのアプリケーションクライアントIDを指定します。CognitoやGoogleがどのアプリケーションからのリクエストであるかを認識するために使います。 |
scope | アクセスを要求するスコープ(権限)を指定します。ユーザーのどの情報にアクセスできるかを定義するパラメータです。追加で設定をすればGoogleアカウントに紐づいているプロフ画像も参照できます |
2. 受け取った認証コードを元にアクセストークンを要求する
1の手順の後、/auth/google/callback
のURLにクエリパラメータとしてcode
が付与されて返ってきます。
http://<自身のアプリケーションのドメイン>/auth/google/callback?code=ae56c4fe-f085-4449-b319-b5d21bcfaed9
この時に初めてhandleOAuthCallback
メソッドが呼び出されます。
このcode
の値を用いて、トークンエンドポイント(/oauth/token
)に対してアクセストークンをリクエストします。
class GoogleAuthController extends Controller
{
private $googleAuthClient;
#...省略
public function handleOAuthCallback(CreateGoogleUser $createGoogleUser)
{
$code = request()->input('code');
# 1.コールバックされたトークンを元にアクセストークンを取得
$getTokenResponse = $this->googleAuthClient->getToken($code);
# ...省略
class GoogleAuthClient
{
# ... 省略
public function getToken(string $code)
{
$endPoint = $this->cognitoDomain . "/oauth2/token";
$data = [
'grant_type' => 'authorization_code',
'client_id' => $this->appClientId,
'client_secret' => $this->appClientSecret,
'code' => $code,
'redirect_uri' => $this->redirectUrl
];
$header = 'Content-type: application/x-www-form-urlencoded';
$response = $this->httpClient->post($endPoint, $data, $header, false);
return json_decode($response, true);
}
class HttpClient
{
# ... 省略
public static function post(
string $url,
array $data,
string $header = 'Content-type: application/json; charset=UTF-8',
bool $isJson = true
){
$content = $isJson
? json_encode($data)
: http_build_query($data);
// ストリームコンテキストオプションの設定
$options = [
'http' => [
'method' => 'POST',
'header' => $header,
'content' => $content, // データを送信
'ignore_errors' => true // エラーを無視してレスポンスを取得する
]
];
$context = stream_context_create($options);
return file_get_contents($url, false, $context);
}
以上の処理が完了すると、$getTokenResponse
には以下の情報が代入されます。
パラメータ | 説明 |
---|---|
id_token | OpenID Connect(OIDC)フローで使用されるトークン。認証されたユーザーの情報を含むJWT(JSON Web Token)。 |
access_token | APIにアクセスする際に用いるトークン。認可されたリソース(ユーザーのプロフィール、カレンダーなど)にアクセスするために使われる。 |
refresh_token |
有効期限が切れたaccess_token を再取得するためのトークン。refresh_token を使うことで、再度ユーザーの許可を得ることなく新しいaccess_token を取得できる。 |
expires_in |
access_token の有効期限を示す値です。単位は秒(秒数)です。 |
token_type | 認証ヘッダー(Authorization ヘッダー)でアクセストークンを指定する際に、どのタイプのトークンとして解釈するかを定義します。 |
3. アクセストークンを元にユーザー情報を受け取る
2で取得したaccess_token
をUserInfo エンドポイント(/oauth/token
)に送信します。
※Authorization: Bearer <access_token>
をheader情報に設定してください
Authorization: Bearer <access_token>
class GoogleAuthController extends Controller
{
# ... 省略
public function handleOAuthCallback(CreateGoogleUser $createGoogleUser)
{
# ... 省略
$getUserInfoResponse = $this->googleAuthClient->getUserInfo($getTokenResponse);
# ... 省略
}
class GoogleAuthClient
{
# ... 省略
public function getUserInfo(array $getTokenResponse)
{
$endPoint = $this->cognitoDomain . "/oauth2/userInfo";
$header = 'Authorization: Bearer '. $getTokenResponse['access_token'];
$response = $this->httpClient->get($endPoint, $header);
return json_decode($response, true);
}
}
class HttpClient
{
public static function get(
string $url,
string $header = 'Content-type: application/json; charset=UTF-8'
){
$options = [
'http' => [
'method'=> 'GET',
'header'=> $header
]
];
$context = stream_context_create($options);
return file_get_contents($url, false, $context);
}
# ... 省略
これらの処理が完了すると、$getUserInfoResponse
に以下の情報が代入されます。
これでやっとCognitoにGoogleユーザーの情報が認知されました!
↑ご覧の通りcognitoにもGoogleユーザーが追加されていることがわかります
4. ユーザー情報をDBに登録
次に、Cognitoのユーザープールの情報をアプリケーションのDBに同期するコードを解説します!(初心者も読むことを想定しているので念のため。)
class GoogleAuthController extends Controller
{
# ... 省略
public function handleOAuthCallback(CreateGoogleUser $createGoogleUser)
{
# ... 省略
$user = $createGoogleUser->execute($getUserInfoResponse);
Auth::login($user);
return redirect(route('home'));
}
class CreateGoogleUser
{
public function execute(array $getUserInfoResponse)
{
return (new User())->newQuery()
->firstOrCreate([
'email' => $getUserInfoResponse['email'],
'cognito_username' => $getUserInfoResponse['username']
]);
}
}
Cognitoにユーザーがすでに存在するか確認し、いなければ新しく登録(サインアップ)、いれば既存ユーザーとして扱います(サインイン)。
最終的に、取得したユーザー情報をアプリケーションのDBに保存し、アプリケーション内でGoogle認証ユーザーを管理できるようにします
ユーザーをログイン状態にし、ホーム画面へリダイレクト
Auth::login($user);
を使ってLaravelの認証機能を用いてユーザーをログイン状態にし、/home
にリダイレクトすれば完了です!!!!!!
まとめ
ここまでお読みいただきありがとうございました。
Google認証の実装について、主要なクラスや処理を簡潔に解説しました。
この手順に従って実装を進めて、皆様のポートフォリオのランクアップの一助になれば幸いです!