来自OAuth代表Azure AD中的流的Base64 SAML断言无效

来自OAuth代表Azure AD中的流的Base64 SAML断言无效,azure,oauth-2.0,azure-active-directory,saml-2.0,Azure,Oauth 2.0,Azure Active Directory,Saml 2.0,当使用Azure AD并代表Flow将OAuth访问令牌交换到SAML断言时,我遇到了一个奇怪的问题。我正在尝试使用Azure AD的代表流将OAuth访问令牌交换到SAML断言 设置 使用OAuth访问令牌与后端通信的前端 我需要从中获取数据的数据源,它受SAML保护 从数据源获取数据的请求需要从后端执行,因为存在对数据源的访问限制 说明 根据Azure AD v1()的文档,我能够请求一个最初看起来很好的响应。我使用的请求参数包括: grant_type: urn:ietf:params

当使用Azure AD并代表Flow将OAuth访问令牌交换到SAML断言时,我遇到了一个奇怪的问题。我正在尝试使用Azure AD的代表流将OAuth访问令牌交换到SAML断言

设置

  • 使用OAuth访问令牌与后端通信的前端
  • 我需要从中获取数据的数据源,它受SAML保护
从数据源获取数据的请求需要从后端执行,因为存在对数据源的访问限制

说明

根据Azure AD v1()的文档,我能够请求一个最初看起来很好的响应。我使用的请求参数包括:

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
access\u 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-D“response.txt”
输入流中的字符无效。

https://sts.windows.net/[id]/。。。test@domain.com断言是您在对AAD的初始调用中收到的访问令牌,如前所述

这是一个JWT令牌,它是基于64 URL编码的,可以使用诸如或或使用任何编程语言之类的工具进行解码。主要的一点是,如果发出的访问令牌是有效的访问令牌,则应该对其进行解码,这与在随后的获取SAML令牌的调用中添加的访问令牌相同

您还可以查看以下有关海外建筑运营管理局流量的文章:

这里需要注意的要点是我们如何从AAD请求初始访问令牌。如果您的前端是SPA,并且您在那里使用隐式流量,您可能想看看“从2018年5月起,某些隐式流量衍生的id_令牌不能用于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

谢谢你的回答。我不知道有未添加的base64字符串(表示base64url字符串)。您为我指出了正确的方向,但是
WebEncoders
类在解码SAML断言时做了同样的事情。它可以这样使用:
var data=WebEncoders.Base64UrlDecode(accessToken)
$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
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();
            }
        }