Invalid base64 saml assertion when obtained in OAuth on-behalf-of flow

olifantastic 26 Reputation points
2021-02-22T09:51:24.797+00:00

Summary
My task is to receive a valid SAML Assertion from an existing OAuth access token on Azure AD. The purpose of this is, to access a SAML protected API using the users credentials. Changing the API to OAuth is not an option.

Description
Following the documentation for Azure AD Api V1 (v1-oauth2-on-behalf-of-flow.md), I was able to get a SAML Assertion. I requested the assertion also following:

  • grant_type: urn:ietf:params:oauth:grant-type:jwt-bearer
  • assertion: <access-token-from-user-requesting-access-to-saml-api>
  • client_id: <client-id-which-requests-resource>
  • client_secret: <assigned-secret>
  • resource: <resource-which-accept-saml-assertion>
  • requested_token_use: on_behalf_of
  • requested_token_type: urn:ietf:params:oauth:token-type:saml2

In addition to that I added x-www-form-urlencoded as content type. This request is sent to "https://login.microsoftonline.com/<tenant-id>/oauth2/token" as HTTP POST. The setup I have is:

  • A React Front-End which authenticates the user on Azure AD using OAuth.
  • A Back-End for the Front-End, which requests the SAML Assertion on-beahlf-of the user (performing the above described request).
  • An API exposing data which the Front-End displays.

Problem

I receive a valid response from Azure AD containing all the necessary fields, which looks like this:

"token_type": "Bearer",
"expires_in": "3579",
"ext_expires_in": "3579",
"expires_on": "1613985579",
"resource": "spn:saml-test",
"access_token": "PEFzc2Vyd...9uPg",
"issued_token_type": "urn:ietf:params:oauth:token-type:saml2",
"refresh_token": "0.ATEAt...SJto5hclkg-7g"

The received assertion in the access_token field is not a valid base64 string (which it should be IMO). Feeding this string to the C# Base64Convert function, results in an exception:

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.
   at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
   at System.Convert.FromBase64String(String s)
   at Validator.Program.Main(String[] args) 

I was however able to decode it using base64 -D on my terminal, which gave me a almost valid SAML Assertion, however it also complained about invalid characters in the string. Actually the only wrong character was the last closing bracket (>) in the XML document. Correcting that manually and converting it into a base64 string to compare it with the received assertion showed me the following errors in the initially received assertion:

  • At the end of the string should be two equal signs
  • All - characters should be + characters

Furthermore, the received SAML Assertion is also not accepted by the SAML API, causing a similar exception.

Question
Am I doing something wrong to obtain the Assertion? Or is this a known issue with V1 OBO Flow? I prefer not to modify the received Assertion, and just to pass it to the SAML API. Is there a known workaround for this?

Microsoft Entra ID
Microsoft Entra ID
A Microsoft Entra identity service that provides identity management and access control capabilities. Replaces Azure Active Directory.
20,189 questions
{count} vote

Accepted answer
  1. Siva-kumar-selvaraj 15,591 Reputation points
    2021-02-26T13:32:47.693+00:00

    Hello @olifantastic ,

    Thanks for reaching out.

    I don’t see anything wrong with above steps that you are referring, I had followed the same steps to test this OAuth on-behalf-of flow through Postman tool and was able to get success assertion as shown below, with that I had used Fiddler’s TextWizard feature (Transform = From Base64) (see below screenshot for your reference) which decoded token just fine and the tool available at https://www.freeformatter.com/base64-encoder.html.

    I had found below alternate way while doing further research (I wasn't tested this one on my lab), but am yet to get more insight on this from our product team, meanwhile I would recommend you to try either of way and see if that works for your environment, fiddler way or Online tool or below workaround.

    Workaround:
    Doing this in C# code may not work: byte[] data = Convert.FromBase64(customBase64);
    Alternatively. try to use the HttpServerUtility.UrlTokenDecode method, it expects that at the end of the data it contains the number of paddings.
    The right code:
    if (base64String.Length % 4 != 0) base64String += (4 – base64String.Length % 4);
    else base64String += 0;
    var data = Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(base64String));

    Hope this helps.

    Request:

    72427-image.png

    Response:

    {  
        "token_type": "Bearer",  
        "expires_in": "3455",  
        "ext_expires_in": "3455",  
        "expires_on": "1614346716",  
        "resource": "api://cdb02891-7976-45b0-91e2-d71157c4d2e0",  
        "access_token": "PEFzc2VydGlvbiBJRD0iXzQzNWU1MWRjLWYxM2YtNDRkNi04MjA3LWU3ZWUwOTM2MTcwMCIgSXNzdWVJbnN0YW50PSIyMDIxLTAyLTI2VDEyOjQxOjAwLjUwNFoiIFZlcnNpb249IjIuMCIgeG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxJc3N1ZXI-aHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZTNiY2RhMGMtZGFmYi00NjQwLTk3NWEtM2FmOGQxZDYzYTI3LzwvSXNzdWVyPjxTaWduYXR1cmUgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxTaWduZWRJbmZvPjxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8-PFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiLz48UmVmZXJlbmNlIFVSST0iI180MzVlNTFkYy1mMTNmLTQ0ZDYtODIwNy1lN2VlMDkzNjE3MDAiPjxUcmFuc2Zvcm1zPjxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L1RyYW5zZm9ybXM-PERpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3NoYTI1NiIvPjxEaWdlc3RWYWx1ZT5kNTRJcW9QeU5SOWcyR1hqQVlVRHE4OE5CaEJIT1YrMjJMYXpaaWJ4YmxjPTwvRGlnZXN0VmFsdWU-PC9SZWZlcmVuY2U-PC9TaWduZWRJbmZvPjxTaWduYXR1cmVWYWx1ZT5rTzZlejRUUEQwMnlGYXRoOC9lVWYxMGwrMVAzQkE1eExyQktLSTRiL1lIMUpTR1d0V0o5a0J0bWdVUVlkOFQ2bWZPenVtUGRid20zcHN5dUUwSG9wekJGZTd0eXBzcFlCeWxPTWFhc281eXN1RmtGN1Z3MG5HV3RjQ0h2aVYwc2FCdnlFNENUN0F5WDM2KzY4UWU5bGpva3VDYlQyeXBZQ1VTNEZpMUJzUGRFOWp0L2NjUnZlcEZaVzNsNHUyYngyRlEzcGl0QzBrYzh1SmN5NmJlZG9vL2JlSFo3S1Y1dW9FcXBxRzBEenF3MEVsakxEaHFrKyt4cDFGSi9sSEVDeVRmU2NQN002b0JueElwSWREaFN3M2JjNXM2YnlrSTRxMUVrUXdYTkk3akRhQ1lmS3NxSFZ3RVZiTkZHbW12TXVRTHFodEZWS0lKcTl0TnRSQnhGaXc9PTwvU2lnbmF0dXJlVmFsdWU-PEtleUluZm8-PFg1MDlEYXRhPjxYNTA5Q2VydGlmaWNhdGU-TUlJREJUQ0NBZTJnQXdJQkFnSVFOMzNST2FJSjZiSkJXREN4dG1KRWJqQU5CZ2txaGtpRzl3MEJBUXNGQURBdE1Tc3dLUVlEVlFRREV5SmhZMk52ZFc1MGN5NWhZMk5sYzNOamIyNTBjbTlzTG5kcGJtUnZkM011Ym1WME1CNFhEVEl3TVRJeU1USXdOVEF4TjFvWERUSTFNVEl5TURJd05UQXhOMW93TFRFck1Da0dBMVVFQXhNaVlXTmpiM1Z1ZEhNdVlXTmpaWE56WTI5dWRISnZiQzUzYVc1a2IzZHpMbTVsZERDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBS0dpeTAvWVpIRW85clJuMmJJMjd1MTg5U3E3TktoSW5GejVoTENTamdVQjJybWY1RVROUjNSSklEaVcxTTUxTEtST3NUcmprbDQ1Y3hLNmdjVndMdUVncjNMMVRnbUJ0ci9SdC9yaUt5eGVYYkxROUxHQndhTlZhSnJTc2N4ZmRGYkphNUorcXpVSUZCaUZvTDdrRThadGJrWkpXQlR4SEV5RWNOQzUySko4eWRPaGd2Wll5a2V0ZThBQVZhMlRaQWJnNEVDbzkrNm5Nc2FHc1NCbmNSSEpsUldWeWNxOFE0SFY0ZmFNRVptWitpeUNaUm8yZlp1ZlhwbjdzSndaN0NFQnV3NHF5Y0h2VWw2eTE1M3NVVUZxc3N3blpHR2pxcEtTcTdJN3NWSTl2akIxOTlSYXJIYVNTYkRnTDJGeGptQVNpVVk0UnF4blRqVmEyWFZIVXdVQ0F3RUFBYU1oTUI4d0hRWURWUjBPQkJZRUZJNW1ONWZ0SGxvRURWTm9JYThzUXM3a0pBZVRNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUJuYUdub2p4TmduVjQrVENQWjlicjRveDFuUm45dHpZOGI1cHdLVFcyTWNKVGUweUV2ckh5YUl0SzhLYm1lS0pPQnZBU2YrUXdIa3ArRjJCQVh6UmlUbDRaK2dORlFVTFB6c1FXcG1LbHo2ZklXaGM3a3NncFRrTUs2QWFUYndXWVRmbXBLblF3L0tKbS82cmJvTERXWXlLRnBRY1N0dTY3UlorYVJ2UXo2OEV2MmdhNUpzWGxjT0ozZ1AvbEU1V0MxUzByamZhYnpkTU9HUDhxWlFoWGs0d0JPZ3RGQmFpc0RuYmpWNXBjSXJqUlBsaG9DeHZLZ0MvMjkwblo5L0RMQkgzVGJIazh4d0hYZUJBbkFqeUFxT1ppajkydWtzQXY3WkxxNE1PRGNuUXNoVklOWHdzWXNoRzFwUXFPTHdNZXJ0TmFZNVd0cnViTVJrdTQ0RHc3UjwvWDUwOUNlcnRpZmljYXRlPjwvWDUwOURhdGE-PC9LZXlJbmZvPjwvU2lnbmF0dXJlPjxTdWJqZWN0PjxOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDpwZXJzaXN0ZW50Ij5XVDRwNVZTSnc4WGVIUnVkVDJiMUVXVVZ3Y3NMTGFlZklubkhGaDBNWm9nPC9OYW1lSUQ-PFN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDIxLTAyLTI2VDEzOjM4OjM2LjQxOVoiIFJlY2lwaWVudD0iaHR0cHM6Ly9hcGliIi8-PC9TdWJqZWN0Q29uZmlybWF0aW9uPjwvU3ViamVjdD48Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMjEtMDItMjZUMTI6MzY6MDAuNDE5WiIgTm90T25PckFmdGVyPSIyMDIxLTAyLTI2VDEzOjM4OjM2LjQxOVoiPjxBdWRpZW5jZVJlc3RyaWN0aW9uPjxBdWRpZW5jZT5hcGk6Ly9jZGIwMjg5MS03OTc2LTQ1YjAtOTFlMi1kNzExNTdjNGQyZTA8L0F1ZGllbmNlPjwvQXVkaWVuY2VSZXN0cmljdGlvbj48L0NvbmRpdGlvbnM-PEF0dHJpYnV0ZVN0YXRlbWVudD48QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vaWRlbnRpdHkvY2xhaW1zL3RlbmFudGlkIj48QXR0cmlidXRlVmFsdWU-ZTNiY2RhMGMtZGFmYi00NjQwLTk3NWEtM2FmOGQxZDYzYTI3PC9BdHRyaWJ1dGVWYWx1ZT48L0F0dHJpYnV0ZT48QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vaWRlbnRpdHkvY2xhaW1zL29iamVjdGlkZW50aWZpZXIiPjxBdHRyaWJ1dGVWYWx1ZT5lODcxZWRhOS1hODI4LTQ0YjItOTZmYy0wZjM2ZDQ4ZmI4MDU8L0F0dHJpYnV0ZVZhbHVlPjwvQXR0cmlidXRlPjxBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZSI-PEF0dHJpYnV0ZVZhbHVlPnNpdmFAYXRyaXVtcGgub25taWNyb3NvZnQuY29tPC9BdHRyaWJ1dGVWYWx1ZT48L0F0dHJpYnV0ZT48QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vaWRlbnRpdHkvY2xhaW1zL2Rpc3BsYXluYW1lIj48QXR0cmlidXRlVmFsdWU-c2l2YTwvQXR0cmlidXRlVmFsdWU-PC9BdHRyaWJ1dGU-PEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL2lkZW50aXR5L2NsYWltcy9pZGVudGl0eXByb3ZpZGVyIj48QXR0cmlidXRlVmFsdWU-aHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZTNiY2RhMGMtZGFmYi00NjQwLTk3NWEtM2FmOGQxZDYzYTI3LzwvQXR0cmlidXRlVmFsdWU-PC9BdHRyaWJ1dGU-PEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL2NsYWltcy9hdXRobm1ldGhvZHNyZWZlcmVuY2VzIj48QXR0cmlidXRlVmFsdWU-aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2F1dGhlbnRpY2F0aW9ubWV0aG9kL3Bhc3N3b3JkPC9BdHRyaWJ1dGVWYWx1ZT48QXR0cmlidXRlVmFsdWU-aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9jbGFpbXMvbXVsdGlwbGVhdXRobjwvQXR0cmlidXRlVmFsdWU-PC9BdHRyaWJ1dGU-PC9BdHRyaWJ1dGVTdGF0ZW1lbnQ-PEF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAyMS0wMi0yNVQxOToxNjo0MS45MTdaIj48QXV0aG5Db250ZXh0PjxBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvQXV0aG5Db250ZXh0Q2xhc3NSZWY-PC9BdXRobkNvbnRleHQ-PC9BdXRoblN0YXRlbWVudD48L0Fzc2VydGlvbj4",  
        "issued_token_type": "urn:ietf:params:oauth:token-type:saml2",  
    

    }

    Fiddler:
    72515-image.png

    ------------------------------------------------------------------------------------------------------------------------------

    Please "Accept the answer" if the information helped you. This will help us and others in the community as well.


0 additional answers

Sort by: Most helpful