Google WorkspaceにおけるSAML挙動：AnthnInstant属性のタイムスタンプ

# 結論
Google Workspace (GWS)をSAML認証におけるIDプロバイダとして利用する際、SAMLアサーション内のAuhtnInstant属性におけるタイムスタンプが更新されず、かなり古い日時を示す状況が発生します。そのためサービス側では、このタイムスタンプが直近であること＝IDプロバイダで認証が最近されていること、を期待した処理をしていると正しく処理が行えません。

# SAML認証の仕様みたいな話
SAML規格を理解するためのリソースとして、AzureADのドキュメントが参考になりました。
https://docs.microsoft.com/ja-jp/azure/active-directory/develop/single-sign-on-saml-protocol#authnstatement

## AuthnInstant属性について
IDプロバイダからサービス側に送信されるSAMLレスポンスに含まれる、AuthnStatement要素内のAuthnInstant属性です。

```SAMLレスポンスの部分的な抜き出し.xml
<saml2:AuthnStatement AuthnInstant="2021-09-06T09:01:51.000Z" SessionIndex="DUMMYSESSIONINDEX">
```

このタイムスタンプは規格上、ユーザーがIDプロバイダで認証された日付時刻を示しているとされます。

> AuthnInstant 属性は、Azure AD でユーザーが認証された時刻を指定します。
https://docs.microsoft.com/ja-jp/azure/active-directory/develop/single-sign-on-saml-protocol#authnstatement

## AuthnInstant属性のタイムスタンプの検証
つまりこのタイムスタンプが古い場合は、IDプロバイダ側で最近認証が行われていないわけですから、サービス側としては、AuthnInstant属性のタイムスタンプが古いSAMLレスポンスは、セキュリティ的によろしくなさそうな気がしますので、これを拒否したくなる気持ちが生まれます。

## 強制的にIDプロバイダでの認証を要求する
サービス側からIDプロバイダ側に送られるSAMLリクエストにおいては、IDプロバイダでの認証を強制させることが可能です。それがAuthnRequest要素内のForceAuthn属性です。

> ForceAuthn　省略可能　これはブール値です。 true の場合は、ユーザーが Azure AD で有効なセッションを持っている場合であっても、再認証を強制されることを意味します。
https://docs.microsoft.com/ja-jp/azure/active-directory/develop/single-sign-on-saml-protocol#authnrequest

SAMLリクエストでこれを送り付ければIDプロバイダで認証が強制され、SAMLレスポンス内のAuthnInstant属性のタイムスタンプが更新されるわけですから、この属性を利用しているサービス側としては、AuthnInstant属性のタイムスタンプが古いSAMLレスポンスは、ますますこれを拒否したくなる気持ちが生まれます。

# Google WorkspaseのSAMLレスポンス挙動
ところが、GWSをIDプロバイダとして利用している場合、サービス側に送信されているSAMLレスポンスの中身を見ていると、AuthnInstantのタイムスタンプが全然更新されません。サービス側でAuthnInstant属性のタイムスタンプが古いSAMLレスポンスを拒否していると、処理が通らないわけです。

```3か月以上前のAuthnInstant.xml
<saml2:AuthnStatement AuthnInstant="2021-09-06T09:01:51.000Z" SessionIndex="DUMMYSESSIONINDEX">
```

ただし、AuthnInstantのタイムスタンプが更新されない場合でも、Conditions要素に含まれるNotBefore属性とNotOnOrAfter属性については、SAMLレスポンスが出力された日付を反映しています。

```3か月以上前のAuthnInstantの時のConditions.xml
<saml2:Conditions NotBefore="2021-12-15T07:11:55.254Z" NotOnOrAfter="2021-12-15T07:21:55.254Z">
```

というわけで以上です。

# 推測
しかし下手をすると、AuthnInstantのタイムスタンプは数カ月以上前になっている場合があって、さすがに何カ月もGWSで認証走っていないってどういうことなのか・・・。なぜこの状況になるのか理由がまったくわかりません。なので断片的に掴んだ事実を列挙しておきます。

## そもそもGoogle Workspaseは`ForceAuthn="true"`を無視してるのでは？
SAMLリクエストで`ForceAuthn="true"`を指定しても、帰ってくるSAMLレスポンス内部のAuthnInstantのタイムスタンプは更新されないので、挙動との整合性があります。
もちろんドキュメントにはそんなこと書いてないので確証は無いですが、海外の方でも同様の疑問を持っている人がいました。
> When we send this SAML request to the Google IdP, the problem is that the IdP server doesn't ask credentials to the end user and redirect directly to the application with a successful response.
> Is that normal ? Does the Google IdP support the attribute ForceAuthn="true" ? I didn't find any documentation on this topic.
https://stackoverflow.com/questions/67776825/use-the-saml-attribute-forceauthn-with-google-idp

## ChromeブラウザがSSOの時は認証キャッシュが利用されているのでIDプロバイダでは認証が行われていない？
Chromeブラウザにはブラウザ自体にログイン機能があり、GWSのユーザーでログインすることでお気に入り等の同期機能が利用でき、同時にGoogle（GWS）へのSSOの状態になります。
ChromeブラウザがGWSにログインしている場合は、IDプロバイダへの認証の要求が発生した場合でも、SSOのおかげでGWSに認証のリダイレクトが発生しません。ブラウザ内部の認証キャッシュみたいなものを利用しているような気がします。
もしもそうだとすると、確かにGWS本体は認証を処理していませんので、AuthnInstantのタイムスタンプが更新されないのもわかります。
ちなみにChromebookの場合は、OS側のログインによりChromeブラウザもSSO、GWS各種サービスもSSOなので、同じ状況です。

## ブラウザ内部の認証キャッシュは、GWS側のセッション有効期限の影響を受けない？
GWSにはセッション有効期限という設定項目があって、設定値の最大は14日間です。この設定だけ見ると、14日経過していれば、セッションが無効なのですから、SSOだろうとなんだろうとGWS側で認証が発生して、AuthnInstantのタイムスタンプが更新されそうな気がします。が、先に述べたようにAuthnInstantのタイムスタンプは平気で数カ月前を示したりするので、この設定項目も効いていないように見えてしまいます。
こうなるとブラウザ内部の認証キャッシュは、GWS側のセッション有効期限の影響を受けていないように見えています。

## まとめ
それで上記の挙動を追求するテストをしようと思いましたけど、構成が複雑だし時間経過の概念が含まれて面倒なので、とりあえずAuthnInstantのタイムスタンプを検証しなければ問題は回避できるのでそれで良しとします。

認証の処理と言うのはシステム的にも負荷が高いところなので、Googleの工夫として、できるだけ認証の回数が節約されるような処理がされているのではないかと理由を推測しています。

### 宿題
Azure ADの時はどうなるのだろうか？