簡體   English   中英

來自 OAuth 的 Base64 SAML 斷言無效,代表 Azure AD 中的流

[英]Invalid Base64 SAML Assertion from OAuth on-behalf-of flow in Azure AD

使用 Azure AD 和 On-Behalf-Of Flow 將 OAuth 訪問令牌交換為 SAML 斷言時,我遇到了一個奇怪的問題。 我正在嘗試使用 Azure AD 的代表流程將 OAuth 訪問令牌交換為 SAML 斷言。

設置

  • 前端使用 OAuth 訪問令牌與后端通信
  • 我需要從中獲取數據的數據源,受 SAML 保護

從數據源獲取數據的請求需要從后端執行,因為對數據源存在訪問限制。

描述

按照 Azure AD v1 的文檔( Github 文檔),我能夠請求最初看起來不錯的響應。 我使用的請求的參數是:

grant_type: urn:ietf:params:oauth:grant-type:jwt-bearer
assertion: <access token containing the correct scopes for the Back-End>
client_id: <client-id-of-back-end>
client_secret: <assigned-secret>
resource: <resource-of-the-datasource>
requested_token_use: on_behalf_of
requested_token_type: urn:ietf:params:oauth:token-type:saml2

請求作為 POST 請求發送,使用x-www-form-urlencoded作為內容類型(端點“https://login.microsoftonline.com/tenant-id/oauth2/token”)。

問題

我幾乎可以肯定,我遇到了一個錯誤,但是我不知道如何在沒有開發人員支持計划的情況下聯系 Azure。 我收到的回復起初看起來不錯:

{
    "token_type": "Bearer",
    "expires_in": "3579",
    "ext_expires_in": "3579",
    "expires_on": "1613985579",
    "resource": "<datasource>",
    "access_token": "PEFzc2Vyd...9uPg",
    "issued_token_type": "urn:ietf:params:oauth:token-type:saml2",
    "refresh_token": "0.ATEAt...hclkg-7g"
}

access_token字段的斷言不是有效的 base64 字符串。 嘗試使用 C# Base64Convert對其進行解碼,會導致此異常:

System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.

然而,我能夠使用 bashs base64 -D對其進行部分解碼,這給了我一個有效的斷言:

$ base64 -D "response.txt"
Invalid character in input stream.
<Assertion ID="_26be6964-2e17-4184-8ac7-d4cdbb9d5700" IssueInstant="2021-02-22T12:35:49.919Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>https://sts.windows.net/[id]/</Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_26be6964-2e17-4184-8ac7-d4cdbb9d5700"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>...<Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"><AttributeValue>test@domain.com</

問題

我幾乎可以肯定,斷言應該是一個有效的 base64 字符串,以便使用任何能夠這樣做的東西進行解碼。 我錯過了什么嗎? 或者這是 V1 OBO Flow 的一個已知問題? 是否有已知的解決方法?

斷言是您在首次調用 AAD 時收到的訪問令牌,如此所述。

This is a JWT token which is Based64 URL encoded and can be decoded using tools like https://JWT.io or https://JWT.ms or using any programming language too. 要點是,如果頒發的訪問令牌是有效的訪問令牌,它應該被解碼,並且這與在后續調用中添加以獲取 SAML 令牌的訪問令牌相同。

您還可以查看以下有關 OBO 流程的文章: https://blogs.aaddevsup.xyz/2019/08/understanding-azure-ads-on-behalf-of-flow-aka-obo-flow/

這里要注意的要點是我們如何從 AAD 請求初始訪問令牌。 如果您的前端是 SPA 並且您在那里使用隱式流,您可能想看看這個“截至 2018 年 5 月,一些隱式流派生的 id_token 不能用於 OBO 流。單頁應用程序 (SPA) 應將訪問令牌傳遞給中間層機密客戶端,以改為執行 OBO 流。

在解碼 JWT 時,首先需要將其從 Base64URL 編碼字符串轉換為 Base64 編碼字符串。 一旦 JWT 編碼為 base64,則需要對其進行解碼,然后再將其解析為 json。

Powershell 樣品相同:

$token = "<put the jwt here>"

if (!$token.Contains(".") -or !$token.StartsWith("eyJ")) { 
    Write-Error "Invalid token" -ErrorAction Stop 
}

 # Token
    foreach ($i in 0..1) {
        $data = $token.Split('.')[$i].Replace('-', '+').Replace('_', '/')
        switch ($data.Length % 4) {
            0 { break }
            2 { $data += '==' }
            3 { $data += '=' }
        }
    }

    $decodedToken = [System.Text.Encoding]::UTF8.GetString([convert]::FromBase64String($data)) | ConvertFrom-Json 
    Write-Verbose "JWT Token:"
    Write-Verbose $decodedToken

C# 樣品:

static void jwtDecoder()
        {
            try
            {
                Console.WriteLine("JWT to Decode: " + jwtEncodedString + "\n");

                var jwtHandler = new JwtSecurityTokenHandler();
                var readableToken = jwtHandler.CanReadToken(jwtEncodedString);

                if (readableToken != true)
                {
                    Console.WriteLine("\n\nThe token doesn't seem to be in a proper JWT format.\n\n");
                }

                if (readableToken == true)
                {
                    var token = jwtHandler.ReadJwtToken(jwtEncodedString);

                    var headers = token.Header;
                    var jwtHeader = "{";
                    foreach (var h in headers)
                    {
                        jwtHeader += '"' + h.Key + "\":\"" + h.Value + "\",";
                    }
                    jwtHeader += "}";
                    Console.Write("\nHeader :\r\n" + JToken.Parse(jwtHeader).ToString(Formatting.Indented));

                    var claims = token.Claims;
                    var jwtPayLoad = "{";
                    foreach (Claim c in claims)
                    {
                        jwtPayLoad += '"' + c.Type + "\":\"" + c.Value + "\",";
                    }
                    jwtPayLoad += "}";
                    Console.Write("\r\nPayload :\r\n" + JToken.Parse(jwtPayLoad).ToString(Formatting.Indented));

                    var jwtSignature = "[RawSignature: ";
                    jwtSignature += token.RawSignature;
                    jwtSignature += " ]";
                    Console.Write("\r\nSignature :\r\n" + jwtSignature);

                    //Console.ReadLine();
                }
            }
            finally
            {
                Console.Write("\n\nPress Enter to close window ...");
                Console.Read();
            }
        }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM