feature image

2025年12月18日 | ブログ記事

ネイティブアプリでoauthがしたい!

この記事はアドベントカレンダー2025 18日目の記事です

はじめに

こんにちは!! 24Bのmumumuです。
今回はOauthでちょっとしたアプリを作ってみたのでその際に面白かったOauthの仕様について紹介します

できるだけ間違いがないように参考文献を付けながら話していきます。何度かRFCという言葉が登場しますがこれはOauthの仕様を定めた文書のことです。

経緯

私はtrap内でアカウント機能のあるwebアプリを何度か作ったことがあります。
しかし!一度も認証について考えたことがありません。それはtraP内のデプロイ先であるNeoShowcaseが認証を行ってくれ、ヘッダーにユーザー名を加えてくれるからです。(素晴らしい環境ですね)
とはいえ少しは認証に触れてみたい!ということで今回はGoogleのOauth認証を使ってアプリを作ってみました。

どんなアプリでOauth認証を導入してみたか

ところで私は最近 alt + spaceのショートカットをよく使っています。ChatGPTアプリを前面に出したり隠したりできて便利なのでおすすめです。
さて、このネイティブアプリ特有のショートカット機能を使ってalt + cが押されたらGoogle calendarにクリップボードの内容を登録するアプリを作ってみました。
クリップボードから日付っぽい文字列をルールベースで検出し、それを入力欄に自動でセットします。あとは送信ボタンを押すだけで、予定がカレンダーに登録されるという流れです。
当然カレンダーに書き込むにはユーザーの許可が必要なので、ここで OAuth が登場します。

GitHub - mumumu6/QuickCal
Contribute to mumumu6/QuickCal development by creating an account on GitHub.
作ったもの

Oauthってなあに?

Oauthとはアプリケーションにユーザーのデータへ必要な分だけアクセス許可を与える仕組みです。
もしアプリにユーザーのパスワードを教えてしまうと不必要な権限まで与えてしまい、なんでもできてしまいます。
なので最小限必要な事だけ許可されたアクセストークンを認可サーバーが発行します。

(例:googleカレンダーに予定を追加するだけのアプリにアカウントのパスワードを教えてしまったらgoogle driveやgoogle photoへアクセスして情報を取ることも可能になってしまうので カレンダーへの予定の追加のみを許可する)

賢いですね!!

ネイティブアプリのOauthのおおまかな流れ

ネイティブアプリにおけるGoogleのOauthの大まかな流れです。利用する認可サーバーや端末では異なる可能性があります

  1. まずユーザーがアプリ上で「ログイン」や「連携」ボタンを押します。
  2. アプリは認証用のurlを作成してデフォルトブラウザでユーザーに開かせます。
  3. ブラウザでユーザーはアプリケーションの要求する権限を確認し許可を同意します。
  4. 認可サーバーがcodeと呼ばれるものをネイティブアプリが起動しているローカルサーバーに送ってきます。
  5. codeを受け取ったアプリはそれを認可サーバーに送りアクセストークンと交換してもらいます。
アプリケーションが Google 認証サーバーにトークン リクエストを送信し、認証コードを受け取って、コードをトークンと交換し、トークンを使用して Google API エンドポイントを呼び出します。
© Google / CC BY 4.0

安全のために注意しなければならない点はいくつかあるのですが基本的な流れはこんな感じです。

ネイティブアプリのClient Secretについて

バックエンドを持つアプリケーションとは違いネイティブアプリは配布物(実行ファイル)にすぎないので、アプリに埋め込んだ値は秘密にできません。
そのため client secret のような機密情報を入れても、「本当にそのclient idを持つ正当なアプリからのリクエストかどうか」の確認には使えません。

ネイティブアプリに関するOauthの仕様であるRFC8252(8.5)にもclient_secretをクライアントの証明に用いることは推奨されていません。
ネイティブアプリは public client として扱われ、クライアント自身をシークレットで証明しない前提になっています。

一方で、Google Oauthではネイティブアプリにたいしてもclient secretを要求されます。
サーバーを持たないネイティブアプリは実行ファイルに埋め込む形になると思いますが、セキュリティ的な意味はおそらくないと思います。

デフォルトブラウザで開く理由

Google認証ページをユーザーに表示する方法としてアプリ内でそのぺージを開くという方法(WebView)も考えられます。しかしこれは推奨されていません
どうしてだめなのでしょうか?

資格情報をアプリが盗めてしまう

WebViewはアプリの一部として動作するため、アプリがユーザーの資格情報とCookieを取得してしまう可能性があります。
RFC8252(8.12)でもWebViewは推奨されておりません。
同様の理由からGoogleでも推奨されていません(参考)

本物のサイトか区別できない

WebViewではURLバーが見えないため、ユーザーはそのログイン画面が本物か判断できません。
URLが確認できない状態で資格情報を入力させるのは問題があります。
パスワードはパスワードマネージャーを使って入力しましょう。手入力はNGです。

毎回ログインが必要になる

デフォルトブラウザを起動すれば既にgoogleにログイン済みなのに対してWebViewだとCookieを共有しないため毎回ログインしなおす必要があり、UX的にも問題があります

認可サーバーからのcodeの受け取り方

ユーザーが連携ボタンを押すと認可サーバー(Google)はアプリケーションにアクセストークンと交換できるcodeというものを送信します。
ネイティブアプリはこのcodeをどのように受け取ればいいでしょうか?手法は主に以下の3つです。

カスタム URI スキーム

特徴

端末によっては複数のアプリが同じURLを登録可能だから非推奨らしい
(RFCにそのような文言は見つけられませんでしが非推奨と言っているサイトが多かったです。)

Claimed Https Scheme URI

特徴

ループバック IP アドレス リダイレクト

Google Oauthではどれが推奨されているか

ではGoogle Oauthではどれがいいのでしょうか?
RFC 8252 では claimed HTTPS scheme が推奨されていますが、
GoogleのOauthに関してはプラットフォームごとに推奨される方法が異なり、windowsやmac用のアプリならループバックが推奨されています。(IOSやAndroidはだめ)

GoogleのOauthでは端末によって推奨される手法が異なっていて注意して実装する必要がありそうです。

ループバックリダイレクトの注意

意外な罠なのですがリダイレクトurlをhttp:localhostにするとhostsファイルを編集されることで別のurlに飛ばすことが可能になってしまいます。なので127.0.0.1が推奨されています。

なんで一発でアクセストークンを渡さないの?

codeというのはユーザーが承認すると認可サーバーが送ってくるもので、これと引き換えにアクセストークンを入手できます。
ではなぜ一発でアクセストークンを渡さないのでしょうか?面倒です。

上で紹介したようにユーザーが承認した後の認可サーバーからの返答はブラウザを経由してlocalhostにリダイレクトされます。そのためURLがアドレスバーに表示されてしまい、履歴に残ったり拡張機能や別プロセスに覗き見られる可能性があります。

そこで直接アクセストークンを渡さず引換券としてcodeを渡します。

code はブラウザを経由して渡されるため、第三者に見られる可能性があります。
そのため、code 単体ではアクセストークンに交換できない仕組みが必要になります。
そこで実はPKCEという手法が登場します。

PKCE(ピクシー)について

実は最初の認可リクエスト(ユーザーが連携ボタンを押した)の際にcode_verifierという値を生成し、これをSHA-256でハッシュ化した値を認可サーバーに送り付けています。

最初のアクセスの際

  1. アプリがランダムな文字列code_verifierを生成する
  2. これをSHA-256でハッシュ化してcode_challengeとして認可サーバーに送ります

トークン交換時

  1. codeを送るときにcode_verifierを送る
  2. 認可サーバーはcode_verifierをハッシュ化しcode_challengeと同じ値になることを確認する
  3. 一致していたらアクセストークンを発行する

このようにして初めのアクセスと同一クライアントであることを確認します。
これでcodeを盗まれてもcode_verifierが分からないのでトークン交換ができません。

そのためにわざわざ二回やり取りする必要があったんですね~

アクセストークンとリフレッシュトークンの管理について

ユーザーがアプリを使うたびに毎回Googleの認証画面に遷移してもいいのですが、それではUX的によくありません。
そのため、アクセストークンの有効期限が過ぎていない間は同じものを使いまわしましょう。

ただし、アクセストークンは短命(Googleは1時間)なので、期限が切れたらそのままではAPIを叩けません。
そこで登場するのが リフレッシュトークンです。リフレッシュトークンを持っていれば、ユーザーを再度ログイン画面に飛ばさなくても、 新しいアクセストークンを自動で取り直すことができます。

保存方法

ネイティブアプリはOSに安全に保存しておいてもらいましょう。
Rustではkeyringというライブラリを用いることでOSに保存しておくことが可能です。

保存しておくものは

あたりだと思います。有効期限を確認しリフレッシュトークンの再取得が必要かどうか見極めましょう。

ちなみに

ちなみにリフレッシュトークンを用いてアクセスをすると新しいリフレッシュトークンがもらえる場合は置き換える必要があります。
Googleはネイティブアプリの場合新しいリフレッシュトークンは含まれていないので気にする必要はありません。

最後に

どうでしたか?先人の安全にしようという努力が見えて非常に面白いですよね!!
実はこれだけでは安全とは言えずいくつかの攻撃に対して脆弱になってしまいます。気になる方は調べてみてください。
今回はOauthの特に利用者側について勉強してみたのですがOIDCや認可サーバーも面白そうですね

明日のアドベントカレンダーの担当は@Naru820@comaviusです!


mumumu icon
この記事を書いた人
mumumu

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2024年9月20日
2024年 1-Monthonを開催しました!!
Synori icon Synori
2025年9月30日
2025年 1-Monthonを開催しました!!!
YMAC icon YMAC
2025年9月15日
traPでの一年半を振り返る〜全班所属の体験記(?)〜
gurukun41 icon gurukun41
2025年12月13日
1-Monthon_25で学ぶ動画編集の小技集
hijoushiki icon hijoushiki
2024年3月15日
個人開発として2週間でWebサービスを作ってみた話 〜「LABEL」の紹介〜
Natsuki icon Natsuki
2022年4月5日
アーキテクチャとディレクトリ構造
mazrean icon mazrean
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記