2014-12-04 84 views
7

我正在嘗試爲Office 365構建一個多租戶應用程序,該應用程序專注於SharePoint Online並使用OAuth2通過Azure進行身份驗證。該問題特定於通過Azure登錄進行SharePoint訪問,但只有在使用此API使用OAuth2進行身份驗證時才能找到。構建SharePoint Online的多租戶應用程序O365

在Azure和Office中正確註冊應用程序並在Azure和Office中設置用戶的許多機制雖然有些複雜,卻可以通過正確的時間投入來克服。

即使Azure的基本OAuth2協議使用情況也相對平穩。但是,憑藉SharePoint的「資源」參數讓我的應用程序真正成爲多租戶的阻礙。這顯然需要我的應用程序在完成登錄序列之前瞭解最終用戶的SharePoint根站點URL。我看不出這是怎麼可能的。有人請給我指出正確的方向。

下面是實際的登錄序列的一個樣本:

GET /common/oauth2/authorize?client_id=5cb5e93b-57f5-4e09-97c5-e0d20661c59a 
&redirect_uri=https://myappdomain.com/v1/oauth2_redirect/ 
&response_type=code&prompt=login&state=D79E5777 HTTP/1.1 
Host: login.windows.net 
Cache-Control: no-cache 

當用戶進行身份驗證,這會導致所提供的轉接呼叫,看起來像這樣:

https://myappdomain.com/v1/oauth2_redirect/?code=AAABAAAAvPM1KaPlrEq...{blah*3} 

大至今!三段認證的下一步是回發到/ token端點的POST,以獲取將在所有後續REST調用中使用的實際承載令牌。這只是經典的OAuth2 ...

POST /common/oauth2/token HTTP/1.1 
Host: login.windows.net 
Accept: text/json 
Cache-Control: no-cache 

----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="grant_type" 

authorization_code 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="code" 

AAABAAAAvPM1KaPlrEq...{blah*3} 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="client_id" 

5cb5e93b-57f5-4e09-97c5-e0d20661c59a 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="client_secret" 

02{my little secret}I= 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="redirect_uri" 

https://myappdomain.com/v1/oauth2_redirect/ 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="resource" 

https://contoso.sharepoint.com/ 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 

這裏就是它粘的地方。 'resource'參數是必需的,並且必須指向您想要訪問的用戶特定端點。對於Exchange或Azure,端點始終相同。 (https://graph.windows.nethttps://outlook.office365.com)但是SharePoint對於每個租戶都有不同的端點。您還沒有實際登錄用戶,但已經需要有關用戶的信息但您還沒有。

如果我部署的應用程序版本假定爲'contoso'作爲租戶名稱(如上所述),那麼只有contoso租戶中的用戶才能使用我的應用讀取SharePoint數據。只要fabrikam中的另一個用戶嘗試使用它,我的POST/token端點就會要求獲得對錯誤站點的許可......並且存在問題。

如何才能在用戶實際登錄之前檢測端點爲POST/token端點的正確端點?是否有一些隱藏的信息給我,我可以使用?是否有某種發現可以檢測租戶的根SharePoint URL?或者更好的是,是否有一個終端可以作爲的代表的租戶的家(類似於https://office.microsoft.com/sharepoint)的資源?然後,它可能會從返回的user_id JWT令牌中收集。這與其他服務類似,對於客戶管理來說非常簡單。但是,我沒有看到這一點。

如果沒有對這些問題的明確答案或解決這些問題的辦法,我必須猜測,編寫一個多租戶應用程序無法在SharePoint Online O365中進行身份驗證......並且這看起來似乎沒有對。有人請幫忙!

+2

啊,最後。經過數小時的研究,實驗和開發 - 我偶然發現了一個解決方案!這是Azure/O365 API的一個未記錄的「特性」,所以我想確保每個對此主題感興趣的人都能發現關鍵:顯然通過資源「https://api.office」向Azure端點進行身份驗證。com/discovery /「會生成一個可以在POST to/token步驟中多次使用的CODE - 允許您對」discovery/v1.0/me/services「進行REST調用並遍歷結果,獲取令牌每個人都有相同的代碼值。不客氣:) – 2015-01-26 20:13:45

+0

回顧:[http://yazezo.com/2013/10/how-to-setup-saas-cloud-multi-tenant.html](How設置一個SaaS雲?多租戶,CRM 2011,Outlook2010) – 2015-09-25 16:02:46

回答

5

我想補充細節,以解決上面我的評論簡要提及 - 這將是任何人在Office 365開發多租戶應用程序很重要,尤其是如果應用程序將永遠訪問SharePoint網站,包括OneDrive。

從OAuth 2.0的角度來看,這裏的程序有點不標準,但在多租戶領域有一定意義。關鍵是重新使用從Azure返回的第一個CODE。在這裏跟我來:

首先,我們按照標準的OAuth認證步驟:

GET /common/oauth2/authorize?client_id=5cb5e93b-57f5-4e09-97c5-e0d20661c59a 
&redirect_uri=https://myappdomain.com/v1/oauth2_redirect/ 
&response_type=code&prompt=login&state=D79E5777 HTTP/1.1 
Host: login.windows.net 
Cache-Control: no-cache 

這重定向到Azure的登錄頁面,在用戶登錄如果成功,天青然後調用回你的端點代碼。 :

https://myappdomain.com/v1/oauth2_redirect/?code=AAABAAAA...{ONE-CODE-To-RULE-THEM-ALL}xyz 

現在我們回發到/token終端獲取的實際承載令牌在後續REST調用中使用。再次,這只是傳統的OAuth2 ...但請注意我們如何將/Discovery端點用作資源 - 而不是我們實際用於收集數據的任何端點。另外,我們要求UserProfile.Read範圍。

POST /common/oauth2/token HTTP/1.1 
Host: login.windows.net 
Accept: text/json 
Cache-Control: no-cache 

----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="grant_type" 

authorization_code 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="code" 

AAABAAAA...{ONE-CODE-To-RULE-THEM-ALL}xyz 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="client_id" 

5cb5e93b-57f5-4e09-97c5-e0d20661c59a 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="client_secret" 

02{my little secret}I= 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="redirect_uri" 

https://myappdomain.com/v1/oauth2_redirect/ 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="scope" 

UserProfile.Read 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="resource" 

https://api.office.com/discovery/ 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 

這個主題的響應將包含可用於製造REST調用的/discovery端點的access-token。現在

{ 
    "refresh-token": "AAABsvRw-mAAWHr8XOY2lVOKZNLJ{BAR}xkSAA", 
    "resource": "https://api.office.com/discovery/", 
    "pwd_exp": "3062796", 
    "pwd_url": "https://portal.microsoftonline.com/ChangePassword.aspx", 
    "expires_in": "3599", 
    "access-token": "ey_0_J0eXAiOiJjsp6PpUhSjpXlm0{F00}-j0aLiFg", 
    "scope": "Contacts.Read", 
    "token-type": "Bearer", 
    "not_before": "1422385173", 
    "expires_on": "1422389073" 
} 

,使用這種access-token,查詢/Services端點找出是Office 365的該用戶提供什麼。

GET /discovery/v1.0/me/services HTTP/1.1 
Host: api.office.com 
Cache-Control: no-cache 

----WebKitFormBoundaryE19zNvXGzXaLvS5D 
Content-Disposition: form-data; name="Authorization" 

Bearer ey_0_J0eXAiOiJjsp6PpUhSjpXlm0{F00}-j0aLiFg 
----WebKitFormBoundaryE19zNvXGzXaLvS5D 

結果將包括一個服務結構數組,描述每個端點的各種端點和功能。

{ 
    "@odata.context": "https://api.office.com/discovery/v1.0/me/$metadata#allServices", 
    "value": [ 
     { 
      "capability": "MyFiles", 
      "entityKey": "[email protected]_SHAREPOINT", 
      "providerId": "72f988bf-86f1-41af-91ab-2d7cd011db47", 
      "serviceEndpointUri": "https://contoso-my.sharepoint.com/_api/v1.0/me", 
      "serviceId": "O365_SHAREPOINT", 
      "serviceName": "Office 365 SharePoint", 
      "serviceResourceId": "https://contoso-my.sharepoint.com/" 
     }, 
     { 
      "capability": "RootSite", 
      "entityKey": "[email protected]_SHAREPOINT", 
      "providerId": "72f988bf-86f1-41af-91ab-2d7cd011db47", 
      "serviceEndpointUri": "https://contoso.sharepoint.com/_api", 
      "serviceId": "O365_SHAREPOINT", 
      "serviceName": "Office 365 SharePoint", 
      "serviceResourceId": "https://contoso.sharepoint.com/" 
     }, 
     { 
      "capability": "Contacts", 
      "entityKey": "[email protected]_EXCHANGE", 
      "providerId": "72f988bf-86f1-41af-91ab-2d7cd011db47", 
      "serviceEndpointUri": "https://outlook.office365.com/api/v1.0", 
      "serviceId": "O365_EXCHANGE", 
      "serviceName": "Office 365 Exchange", 
      "serviceResourceId": "https://outlook.office365.com/" 
     } 
    ] 
} 

現在到了棘手的部分...在這一點上,我們知道,我們真的要驗證端點 - 其中一些是租戶特定的。通常情況下,你會認爲我們需要重新演奏這些終結點的OAuth2舞蹈。但是在這種情況下,我們可以使用上面相同的HTTP請求,只是簡單地發佈與我們最初從Azure收到的代碼相同的代碼 - 僅使用上述Service結構中的serviceResourceIdcapability來更改resourcescope字段。就像這樣:

POST /common/oauth2/token HTTP/1.1 
Host: login.windows.net 
Accept: text/json 
Cache-Control: no-cache 

----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="grant_type" 

authorization_code 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="code" 

AAABAAAA...{ONE-CODE-To-RULE-THEM-ALL}xyz 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="client_id" 

5cb5e93b-57f5-4e09-97c5-e0d20661c59a 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="client_secret" 

02{my little secret}I= 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="redirect_uri" 

https://myappdomain.com/v1/oauth2_redirect/ 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="scope" 

MyFiles.Read 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="resource" 

https://contoso-my.sharepoint.com/ 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 

然後做同樣的其他兩個:

... 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="scope" 

RootSite.Read 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="resource" 

https://contoso.sharepoint.com/ 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 

... 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="scope" 

Contacts.Read 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 
Content-Disposition: form-data; name="resource" 

https://outlook.office365.com/ 
----WebKitFormBoundaryE19zNvXGzXaLvS5C 

所有這三個調用將導致像第一篇文章的響應上面的,爲您提供每個相應端點的刷新令牌和訪問令牌。所有這些僅以單一用戶身份驗證的價格提供。 :)

中提琴!神祕解決了 - 您可以爲O365編寫多租戶應用程序。 :)

+2

天才。謝謝!這是我遇到過的最可怕的驗證過程。很高興我發現了這個。 – Jandieg 2015-02-19 01:26:42

+1

「這是最可怕的驗證過程I '曾經處理過 - 「聽到了!+1 – papercowboy 2015-04-20 09:10:38

+0

幹得好!:) - 但要小心,這個CODE的工作時間可能會有一個時間限制。 – Nils 2016-05-18 09:26:31

相關問題