LINEログインのOIDC認可コードフローをSDKを使わず手元から試してみた
リテールアプリ共創部のるおんです。
普段LINEログインを使った認証機能では、SDKやライブラリに任せて「パラメータを設定したら動いた」で済ませていましたが、今回は、SDKや公式ライブラリをあえて使わず 、curl とブラウザだけで LINEログインの OIDC(OpenID Connect)の認可コードフロー を一から自分で組み立てて動かしてみました。各ステップでどんなリクエストが飛んで、何が返ってくるのかを、実物で確認していきます。
個人的には、この 一連の流れを自分で実行してみる と、OAuth/OIDC の登場人物とやりとりが一気に腑に落ちる ので、普段ライブラリ任せで使っている方にこそおすすめです。
先に結論
今回やったこと(curl とブラウザで LINEログインの OIDC 認可コードフローをひと通り動かす)を、先に流れだけまとめます。
- ブラウザで 認可リクエスト を送り、ログイン・同意して 認可コード(code) を受け取る
- その code を
curlで トークンエンドポイント に送り、アクセストークン と IDトークン を受け取る - IDトークンを デコード して、中に「誰がログインしたか(
sub)」などの情報が入っていることを確認する - 受け取った IDトークンが 正しいもの(確かに LINE が発行した、改ざんされていないもの)か 、LINE の 検証エンドポイント で検証する
- 最後に、アクセストークン を使って UserInfo エンドポイント を叩き、プロフィールを取得する(任意)
本来ならこれをアプリケーションでSDK等を用いて実装して動かしますが、今回は手元のブラウザとターミナルのcurlから試します。IDトークンで「誰がログインしたか」を受け取る(OIDCによる認証)だけでなく、受け取った アクセストークン を Authorization: Bearer ヘッダーに載せて UserInfo エンドポイント を叩き、表示名やアイコンなどのプロフィールも取得するところまでやってみます。
準備
- LINE Developers アカウント(LINE Developersコンソール)
curl(ターミナル)python3(IDトークンのデコードに使用。Mac なら標準で入っています)
OIDC のエンドポイントは、LINE が公開している OpenID Configuration で確認できます。最初に叩いておくと、以降で使う URL がすべて分かります。
curl https://access.line.me/.well-known/openid-configuration
返ってくる JSON のうち、今回使うのはこのあたりです。
| フィールド | 値 | 用途 |
|---|---|---|
authorization_endpoint |
https://access.line.me/oauth2/v2.1/authorize |
認可リクエストの送り先(ブラウザ) |
token_endpoint |
https://api.line.me/oauth2/v2.1/token |
code をトークンに交換(curl) |
userinfo_endpoint |
https://api.line.me/oauth2/v2.1/userinfo |
プロフィール取得 |
1. LINEログインチャネル作成
GoogleでいうOAuthクライアント登録にあたる作業です。LINEでは「LINEログインチャネル」を作ります。
- LINE Developersコンソール で プロバイダー を作成(未作成の場合)
- プロバイダー内で 「新規チャネル作成」→「LINEログイン」 を選択して作成
- [チャネル基本設定]タブで チャネルID と チャネルシークレット を控える
- [LINEログイン設定]タブで コールバックURL に
http://localhost:8080/callbackを登録
チャネルIDが OIDC の client_id、チャネルシークレットが client_secret にあたります。

今回はローカルにサーバーを立てず、リダイレクト先のエラー画面の URL から code を手でコピーします。なので、コールバックURL は「動いていない」localhost で問題ありません。
2. 認可リクエスト(ブラウザ)で認可コードを受け取る
まず、ログインを始めるための 認可リクエスト をブラウザから送ります。<チャネルID> を自分の値に置き換えて、次の URL を ブラウザのアドレスバーに貼る だけです。
https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id=<チャネルID>&redirect_uri=http://localhost:8080/callback&state=abc123&scope=openid%20profile&nonce=xyz789
クエリパラメータの意味はこうです。
| パラメータ | 値 | 意味 |
|---|---|---|
response_type |
code |
認可コードがほしい、という宣言 |
client_id |
チャネルID | どのアプリ(チャネル)からのリクエストか |
redirect_uri |
コールバックURL | 終わったらどこに戻すか(登録値と完全一致) |
state |
abc123 |
CSRF対策。リクエストとコールバックの対応を確認する |
scope |
openid profile |
要求する権限(%20 はスペース) |
nonce |
xyz789 |
リプレイ対策。あとで IDトークンの中に同じ値が入る |
この URL をブラウザで開くと、LINE のログイン画面 → 同意画面(openid:内部識別子 / profile:プロフィール)が出ます。許可すると、LINE は ステータスコード302 で、登録したコールバックURLにリダイレクトしてきます。


http://localhost:8080/callback?code=(認可コード)&state=abc123
ローカルにサーバーがないので、ブラウザには「このサイトにアクセスできません」というエラー画面が出ますが問題ないです。大事なのは見た目ではなく、アドレスバーに入っている code と state です。
stateが、先ほど送ったabc123と 一致 しているか確認する(CSRF対策)codeの値をコピーしておく(次で使う)
ここまでが ブラウザ の担当です。ユーザーがログイン・同意する必要があるので、人間が操作するブラウザでやりとりし、その結果(認可コード)をブラウザ経由で受け取っています。
3. トークンリクエスト(curl)でトークンを受け取る
受け取った code を、今度は トークンエンドポイント に送ってトークンに交換します。ここからは ブラウザではなく curl 、つまり「自分がアプリのサーバー役」になってリクエストを送ります。
<...> を自分の値に置き換えて実行します。
curl -s -X POST https://api.line.me/oauth2/v2.1/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code' \
-d 'code=<取得したcode>' \
--data-urlencode 'redirect_uri=http://localhost:8080/callback' \
-d 'client_id=<チャネルID>' \
-d 'client_secret=<チャネルシークレット>'
成功すると、こんなレスポンスが返ってきます(値はダミー)。
{
"access_token": "(アクセストークン)",
"token_type": "Bearer",
"refresh_token": "(リフレッシュトークン)",
"expires_in": 2592000,
"scope": "profile openid",
"id_token": "(IDトークン。JWT)"
}
アクセストークンは、このあとユーザー情報やその他のリソースにアクセスする際に、Bearer認証で毎回リクエストに含めて送信します。
一方のIDトークンは、「誰がログインしたか」をアプリに伝える 認証 のためのトークンです。アプリはこれを検証して、ログインしたユーザーを特定します。
4. IDトークンをデコードして中身を見る
レスポンスの id_token が、OIDC の主役です。形式は JWT で、ヘッダー.ペイロード.署名 の3つのパートを .(ドット)で繋いだ文字列になっています。
このうち ペイロード(2つ目) をデコードすると、中身が読めます。<id_token> を実際の値に置き換えて実行します。
echo '<id_token>' | cut -d '.' -f2 | python3 -c "import sys,base64,json;p=sys.stdin.read().strip();print(json.dumps(json.loads(base64.urlsafe_b64decode(p+'='*(-len(p)%4))),indent=2,ensure_ascii=False))"
デコードすると、こんな JSON が出てきます(sub などはマスクしています)。
{
"iss": "https://access.line.me",
"sub": "U********************************",
"aud": "<チャネルID>",
"exp": 1782618947,
"iat": 1782615347,
"nonce": "xyz789",
"amr": ["linesso"],
"name": "(表示名)",
"picture": "https://profile.line-scdn.net/..."
}
ここまで来ると、自分が送った値や設定が、そのまま IDトークンの中に入っている のが確認できます。
iss:発行者。https://access.line.me(=LINE)になっているaud:このトークンの宛先。自分のチャネルID になっている(「このトークンは自分のアプリ向けか?」の確認に使う)sub:ユーザーの識別子 。アプリはこの値で「誰がログインしたか」を判断するnonce:手順2で送ったxyz789がそのまま入っている(リプレイ対策が効いている)amr:認証方法。linessoは「LINE のシングルサインオン(既にLINEにログイン済みでそのまま通った)」を表す
5. IDトークンを検証する(verifyエンドポイント)
さきほどのデコードは ただ中身を読んだだけ で、「そのトークンが本物か(確かに LINE が発行した、改ざんされていないものか)」は まだ確かめていません 。検証せずに sub を信用してしまうと、誰かが偽造したトークンを掴まされるおそれがあります。
検証は自前でもできますが、LINE には IDトークン検証用のエンドポイント(/oauth2/v2.1/verify)が用意されています。今回はこれを使い、署名の検証まで含めて LINE 側にやってもらいます 。<...> を自分の値に置き換えて実行します。
curl -s -X POST https://api.line.me/oauth2/v2.1/verify \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'id_token=<id_token>' \
-d 'client_id=<チャネルID>' \
-d 'nonce=xyz789'
| パラメータ | 値 | チェック内容 |
|---|---|---|
id_token |
IDトークン | 署名・有効期限(exp)が正しいか(必須) |
client_id |
チャネルID | トークンの aud がこれと一致するか(必須) |
nonce |
xyz789 |
トークンの nonce がこれと一致するか(任意) |
検証に成功すると、IDトークンのペイロードがそのまま返ってきます 。「中身が読めた」ではなく、「署名・有効期限・aud・nonce のチェックを全部通った」という意味になります。
{
"iss": "https://access.line.me",
"sub": "U********************************",
"aud": "<チャネルID>",
"exp": 1782618947,
"iat": 1782615347,
"nonce": "xyz789",
"amr": ["linesso"],
"name": "(表示名)",
"picture": "https://profile.line-scdn.net/..."
}
逆に、トークンが壊れていたり、有効期限が切れていたり、aud や nonce が一致しないと、ペイロードではなくエラーが返ります 。
6. UserInfo でプロフィールを取る(任意)
「誰がログインしたか」は IDトークンの sub で分かります。表示名やアイコンなど、追加のプロフィール情報がほしいときは、アクセストークン を使って UserInfo エンドポイントを叩きます。
curl -s https://api.line.me/oauth2/v2.1/userinfo \
-H 'Authorization: Bearer <access_token>'
{
"sub": "U********************************",
"name": "(表示名)",
"picture": "https://profile.line-scdn.net/..."
}
アクセストークンをBearerでヘッダーに載せて送信します。
おわりに
今回は、SDKを使わず curl とブラウザだけで、LINEログインの OIDC 認可コードフローをひと通り動かしてみました。やったことを振り返ると、流れはシンプルです。
- 認可リクエスト(ブラウザ) で
codeを受け取る - トークンリクエスト(curl) で
codeをaccess_token/id_tokenに交換する - IDトークンをデコード して「誰がログインしたか」を確認する
- IDトークンを検証(verifyエンドポイント)して、それが正しい(改ざんされていない)トークンか確かめる
- UserInfo でプロフィールを取る
普段 SDK 任せで使っていると、ログインボタンの裏でこのやりとりが走っていることはなかなか意識しません。ですが、一度 curl で通して動かしておくと、SDK が裏で何を肩代わりしてくれているのか がはっきり見えるようになります。LINEログインに限らず、OAuth/OIDC を「雰囲気で」使っている方の、最初の一歩になれば幸いです。
以上、どなたかの参考になれば幸いです。
参考







