#azure #oauth-2.0 #azure-active-directory #saml-2.0
#azure #oauth-2.0 #azure-active-directory #saml-2.0
Вопрос:
Я сталкиваюсь со странной проблемой при обмене токена доступа OAuth на утверждение SAML с использованием Azure AD и от имени потока. Я пытаюсь обменять токен доступа OAuth на утверждение SAML, используя поток от имени Azure AD.
Настройка
- Интерфейс, взаимодействующий с серверной частью с использованием токенов доступа OAuth
- Источник данных, из которого мне нужно получить данные, защищенный SAML
Запрос на извлечение данных из источника данных должен выполняться из серверной части, поскольку существуют ограничения доступа к источнику данных.
Описание
Следуя документации для Azure AD v1 (Github docs), я смог запросить ответ, который изначально выглядит нормально. Параметры для запроса, который я использовал, следующие:
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
типа содержимого as (конечная точка «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.
Однако я смог частично расшифровать его с помощью bash 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 для декодирования с использованием всего, что способно это сделать. Я что-то упустил? Или это известная проблема с потоком OBO версии V1? Существует ли известный обходной путь для этого?
Ответ №1:
утверждение — это токен доступа, который вы получаете при первоначальном вызове AAD, как указано здесь .
Это токен JWT, который кодируется URL-адресом Based64 и может быть декодирован с помощью таких инструментов, как https://JWT.io или https://JWT.ms или с использованием любого языка программирования тоже. Главное, что если выданный токен доступа является действительным токеном доступа, он должен быть декодирован, и это тот же токен доступа, который добавляется при последующем вызове для извлечения токена SAML.
Вы также можете проверить следующую статью, в которой говорится о потоке OBO: https://blogs.aaddevsup.xyz/2019/08/understanding-azure-ads-on-behalf-of-flow-aka-obo-flow /
Здесь следует отметить, что основным моментом является то, как мы запрашиваем начальный токен доступа из AAD. Если ваш интерфейс является SPA, и если вы используете там неявный поток, вы можете захотеть взглянуть на это «По состоянию на май 2018 года некоторый id_token, производный от неявного потока, не может использоваться для потока OBO. Одностраничные приложения (SPA) должны передавать токен доступа конфиденциальному клиенту среднего уровня для выполнения потоков OBO вместо этого «.
При декодировании JWT его сначала необходимо преобразовать в строку в кодировке Base64 из строки в кодировке Base64URL. После того, как 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("nnThe token doesn't seem to be in a proper JWT format.nn");
}
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 :rn" 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("rnPayload :rn" JToken.Parse(jwtPayLoad).ToString(Formatting.Indented));
var jwtSignature = "[RawSignature: ";
jwtSignature = token.RawSignature;
jwtSignature = " ]";
Console.Write("rnSignature :rn" jwtSignature);
//Console.ReadLine();
}
}
finally
{
Console.Write("nnPress Enter to close window ...");
Console.Read();
}
}
Комментарии:
1. Спасибо за ваш ответ. Я не знал, что существуют не добавленные строки base64 (которые представляют строку base64url). Вы указали мне правильное направление, однако
WebEncoders
класс делает то же самое для декодирования утверждения SAML. Его можно использовать следующим образом:var data = WebEncoders.Base64UrlDecode(accessToken);