Skip to main content

Command Palette

Search for a command to run...

ASP.NET Identity Ouath 登入 null 問題

Updated
2 min read
ASP.NET Identity Ouath 登入 null 問題

aspnet-identity-null-reference-logininfo-is-null

最近有幾個案子要辦 Facebook 行銷活動,因為都是小案子,所以就直接用了 Visual Studio 2015 內建的範本,其中就包含了 ASP.NET Identity 功能

在開發與測試時都非常順利,然後就上線了

於上線運作階段偶爾會接到使用者反映使用 Facebook 登入時,點選登入後經過 Facebook 驗證成功,可是又回到了登入頁面

於初期完全無法根據特定條件重現問題,偶爾會發現此問題但是下一次嘗試時登入卻又成功

原因

根據多方 GOOGLE 後,使用關鍵字:asp.net identity, login, owin, null 在 StackOverflow 查到了 ASP.NET MVC 5 (VS2013 final): Facebook login with OWIN fails (loginInfo is null)

在此篇文章中有一個連結 ASP.NET_SessionId + OWIN Cookies do not send to browser (此篇講解為什麼會出問題,並附上落落長的分析,但等等會提供佛心大大提供非常直接的解決方案)

一些背景知識

以下說明根據的 Assembly Versions 為:

  • Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
  • Microsoft.Owin.Host.SystemWeb, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
  • System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

OWIN 使用自己的抽象化方式來實作 Cookie (Microsoft.Owin.ResponseCookieCollection),它實作的方式是直接將 Response Header 集合包起來並從而更新 Set-Cookie Header OWIN ASP.NET Host (Microsoft.Owin.Host.SystemWeb) 直接將 System.Web.HttpResponse 與 Header 集合整個包起來 因此當 OWIN 建立新 Cookie 時,Response 的 Set-Cookie Header 直接被OWIN改成OWIN自己的格式

但 ASP.NET 針對 Response Cookies 也有自己的抽象化方式,可以透過 sealed class System.Web.HttpCookieCollection 中之 System.Web.HttpResponse.Cookies 屬性與實作略為窺知 它的實作方法並不會直接改變 Set-Cookie Header,而是使用了少數最佳化過的 Internal Notifications 來將狀態的變更寫入 Response 物件

在 Requert 的生命週期的特定時間點中,當 HttpCookieCollection 狀態變更完畢 (System.Web.HttpResponse.GenerateResponseHeadersForCookies()) 且 Cookies 已經序列化到 Set-Cookie Header 中 假如這個集合正好處於某些特殊狀態下時,整個 Set-Cookie Header 會先被整個清空後,才根據集合中的 Cookies 重新建立

ASP.NET Session 的實作是使用 System.Web.HttpResponse.Cookies 屬性來儲存 ASP.NET_SessionId Cookie,且在 ASP.NET Session 的模組 (System.Web.SessionState.SessionStateModule) 中有做了一些最佳化,透過一個靜態屬性名為:s_sessionEverSet,根據這個名字就可以大概一窺它的功用;假設你有將任何資料存入至 Session State 中,那麼這個模組就會自動幫你在每個 Request 中多做一點額外的處理

回到問題

透過以上的一些知識就可以來解釋這些靈異現象

Case 1. 從沒使用過Session

System.Web.SessionState.SessionStateModule, s_sessionEverSet 屬性為 false 沒有任何 session id 被 session state 模組產生且 System.Web.HttpResponse.Cookies 集合狀態沒有偵測到任何變更 在這情況下,OWIN Cookies被正確送出給瀏覽器並成功登入

Case 2. Session 在應用程式某處被使用過, 但使用者尚未嘗試登入過

System.Web.SessionState.SessionStateModule 與 s_sessionEverSet 屬性為 true Session Id 由 SessionStateModule 產生,並將 ASP.NET_SessionId 加入至 System.Web.HttpResponse.Cookies 集合中,但在稍後將因 Session 是空的所以被清除掉 在這狀況下 System.Web.HttpResponse.Cookies 集合狀態被偵測到改變,因此 Set-Cookie Header 先被清除後再次將 Cookies 重新序列化到 Header, 因此這樣可以得知 OWIN Response Cookies 其實已經不見了,使用者被判斷為沒有登入並重新導向到登入頁面

Case 3. 再登入之前已經使用過 Session

System.Web.SessionState.SessionStateModule 與 s_sessionEverSet 屬性為 true Session Id 由 SessionStateModule 產生,並將 ASP.NET_SessionId 加入至 System.Web.HttpResponse.Cookies 集合中 因為 System.Web.HttpCookieCollection 內部的最佳化機制,System.Web.HttpResponse.GenerateResponseHeadersForCookies() Set-Cookie Header 並沒有立刻被清除,而只有被更新 在這狀況下 OWIN authentication cookies 與 ASP.NET_SessionId cookie 都被正確送出,因此登入成功

看到這邊你就會突然知道為什麼之前的搜尋結果都說先用 Session 再登入就會成功,這真的是知其然而不知所以然阿!

更普通的說

從上面的說明可以看出這問題不只發生在 ASP.NET session 上,只要你是透過 Microsoft.Owin.Host.SystemWeb 來 Hosting OWIN ,你的 System.Web.HttpResponse.Cookies 都會有機會發生問題

舉例來說 以下為正常運作範例

正常運作

但以下範例就不會正常運作且 OwinCookie 也會遺失

不會正常運作

以上範例使用 VS2013, IISExpress and 預設 MVC project 範本進行驗證

解決方法

在這邊有個大神直接寫了nuget package出來:http://stackoverflow.com/a/26978166

nuget package

安裝nuget套件後,直接在ConfigureAuth方法上設定

app.UseKentorOwinCookieSaver();

如此乾淨俐落的解決,問題再也沒出現過

More from this blog

Career-Ops 安裝與設定 — 用 AI 來幫你篩職缺、客製履歷

最近在看新的工作機會,光是瀏覽職缺、比對 JD、調整履歷就花了不少時間。 後來找到 Career-Ops 這個開源專案 作者 santifer 用這套系統篩了 740 多個職缺、產出 100 多份客製化履歷,最後拿到 Head of Applied AI 的 offer。 看起來蠻有意思的,所以我就在 Windows 上實際裝了一輪,這篇就來把整個安裝和設定的過程記錄下來。 我這邊搭配的 AI 後

Apr 12, 20265 min read
Career-Ops 安裝與設定 — 用 AI 來幫你篩職缺、客製履歷

讓 LLM 自動化你的 N8N 工作流程:n8n-mcp-server 安裝指南

前言 網路上一大堆都是在介紹如何在 N8N 裡面用 MCP 來呼叫其他工具,但是更多時候我希望 LLM 能直接幫我寫好或是修改 N8N 的工作流程 所以這篇就來介紹如何設定與使用 n8n-mcp-server 來讓 LLM 幫忙操控 N8N 在 GitHub 上有不少的 n8n-mcp-server,經過一下下的簡單搜索,我決定使用這個專案: n8n-mcp-server 原因在於說這個專案的實作提供了基本上所有需要編輯工作流程的功能,以下就來記錄安裝過程 MCP Server 設定 首先當然...

May 28, 20253 min read

[IT 鐵人賽] ASP.NET Core 與 Log 紀錄和追蹤的愛恨交織 - Day 05 - Elmah - 02

.NET Core Logging- Elmah 02 上一篇我們提到了基本的使用方式,接下來的這一篇我們就來講講要怎麼把 Log 放到不同的儲存體上,以及如何過濾 Log Log 儲存方式 我們先來看這些 Log 倒底存在哪裡,目前共有三種儲存方式,如下: MemoryErrorLog — store errors in memory 預設為使用此方式,簡單來說就是將錯誤 Log 都存在記憶體裡,所以只要應用程式一重新啟動,Log 就沒了;但是也是最方便的方式,只是要注意如果 Log 太...

Oct 20, 20182 min read
[IT 鐵人賽] ASP.NET Core 與 Log 紀錄和追蹤的愛恨交織 - Day 05 - Elmah - 02

[IT 鐵人賽] ASP.NET Core 與 Log 紀錄和追蹤的愛恨交織 - Day 04 - Elmah - 01

.NET Core Logging- Elmah 01 Elmah 是我最一開始使用的 Log 工具,基本上它的功能就是將應用程式所有發生的錯誤記錄下來,不需要改變程式架構,而且又有介面可以觀看,十分的方便;它從 WebForm 時代就有了,到了 MVC 也是運作的很好,那這邊就來介紹一下 Elmah 要如何在 ASP.NET Core 中使用 (.NET Core 也可使用)。 不過很可惜的,目前並沒有釋出官方的 Elmah for .NET Core,不過有一個新專案: ElmahCore...

Oct 19, 20181 min read
[IT 鐵人賽] ASP.NET Core 與 Log 紀錄和追蹤的愛恨交織 - Day 04 - Elmah - 01

[IT 鐵人賽] Azure Data Service - Day 04 - Cognitive Service - 辨識 - Video Indexer

Azure Data Service - Day 04 - Cognitive Service - Vision - Video Indexer 前面幾篇都是介紹靜態影像的辨識與分析,那這篇就來到針對影片來做處理。 Cognitive Service 針對影片辨識的部分提供了:Video Indexer 它整合了許多功能,在這邊列出一些比較重要的功能: 語系偵測 可以自動偵測出這個影片是屬於哪個語系,目前支援: English, Spanish, French, German, Italia...

Oct 19, 20181 min read
[IT 鐵人賽] Azure Data Service - Day 04 - Cognitive Service - 辨識 - Video Indexer

BrainBurst, HeadFirst

45 posts

學習學到老,撞牆撞到腦. A lifelong quest—with forehead dents.