JRobinss
JRobinss

Reputation: 201

How to retrieve SAML2 token in PHP for getting an OAuth token from WSO2 APIM

I have been looking around for a solution but can't find it.

This question is directly related to:

Here's the scenario I want to execute:

  1. authenticate to the SP using SAML2. Coded in PHP with SimpleSamlPhp.
  2. get an OAuth token from the API mgr endpoint, using the SAML2 assertion. (using URL http://api.gateway/token)
  3. call APIs over the API mgr's gateway, using the OAuth token.

I'm stuck at the instruction: get the SAML2 assertion token. Where do I find this token? In SimpleSamlPhp I can get attributes of the user, or their id, but I can't find any assertion.

I hacked SSP to get the last assertion, but don't know what to do with it. I was expecting a single value (like a token), but it's a complex structure. And one of the links above says I shouldn't be accessing it!

What do I send, encoded, to the token URL?


EDIT: adding some samples. My assert XML (edited):

<?xml version="1.0" encoding="UTF-8"?>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" ID="okdjgbm...jdbh" IssueInstant="2016-11-28T09:49:45.808Z" Version="2.0">
   <saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://g...p.com:9443/samlsso</saml2:Issuer>
   <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
      <ds:SignedInfo>
         <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
         <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
         <ds:Reference URI="#okd...kejdbh">
            <ds:Transforms>
               <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
               <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            </ds:Transforms>
            <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <ds:DigestValue>RrGcR...cktFuH0=</ds:DigestValue>
         </ds:Reference>
      </ds:SignedInfo>
      <ds:SignatureValue>b91j/k...Z7d4=</ds:SignatureValue>
      <ds:KeyInfo>
         <ds:X509Data>
            <ds:X509Certificate>MIICNT...Wq8uHSCo=</ds:X509Certificate>
         </ds:X509Data>
      </ds:KeyInfo>
   </ds:Signature>
   <saml2:Subject>
      <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">[email protected]</saml2:NameID>
      <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
         <saml2:SubjectConfirmationData InResponseTo="_80258326a1...cd4bbd" NotOnOrAfter="2016-11-28T09:54:45.807Z" Recipient="http://1.2.3.4/simplesamlphp/www/module.php/saml/sp/saml2-acs.php/wso2-sp" />
      </saml2:SubjectConfirmation>
      <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
         <saml2:SubjectConfirmationData InResponseTo="_8025832...d4bbd" NotOnOrAfter="2016-11-28T09:54:45.807Z" Recipient="https://my.wso2.apim:8243/token" />
      </saml2:SubjectConfirmation>
   </saml2:Subject>
   <saml2:Conditions NotBefore="2016-11-28T09:49:45.808Z" NotOnOrAfter="2016-11-28T09:54:45.807Z">
      <saml2:AudienceRestriction>
         <saml2:Audience>mytestapp</saml2:Audience>
         <saml2:Audience>https://my.wso2.apim:8243/token</saml2:Audience>
      </saml2:AudienceRestriction>
   </saml2:Conditions>
   <saml2:AuthnStatement AuthnInstant="2016-11-28T09:49:45.815Z" SessionIndex="1f5192...b591">
      <saml2:AuthnContext>
         <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml2:AuthnContextClassRef>
      </saml2:AuthnContext>
   </saml2:AuthnStatement>
   <saml2:AttributeStatement />
</saml2:Assertion>

encode as base64-URL-encoded XML:

PHNhbWwyO...dGlvbj4,

using the method copied from another SO post

function base64_url_encode($input) {
 return strtr(base64_encode($input), '+/=', '-_,');
}

It's about 20 lines long (and yes, there's a comma at the end).

It doesn't work, I get the JSON result

{"error":"invalid_grant","error_description":"Provided Authorization Grant is invalid."}

In the traces:

TID: [0] [AM] [2016-11-29 11:04:50,297] DEBUG {org.wso2.carbon.identity.oauth2.token.handlers.grant.saml.SAML2BearerGrantHandler} -  SAML Token Issuer verification failed or Issuer not registered {org.wso2.carbon.identity.oauth2.token.handlers.grant.saml.SAML2BearerGrantHandler}
TID: [0] [AM] [2016-11-29 11:04:50,298] DEBUG {org.wso2.carbon.identity.oauth2.token.AccessTokenIssuer} -  Invalid Grant provided by the client, id=fkJa...Ohoa, user-name=null to application=myapp-subscriber_test_PRODUCTION {org.wso2.carbon.identity.oauth2.token.AccessTokenIssuer}
TID: [0] [AM] [2016-11-29 11:04:50,300] DEBUG {org.wso2.carbon.identity.oauth2.token.AccessTokenIssuer} -  OAuth-Error-Code=invalid_grant client-id=fkJa...hoa grant-type=urn:ietf:params:oauth:grant-type:saml2-bearer scope=PRODUCTION {org.wso2.carbon.identity.oauth2.token.AccessTokenIssuer}

I'm asking around to the configurators of the system. Seems to me there's something missing in the link between IDS, APIM and OAuth, some declaration. (I googled this, and came up with a stackoverflow exchange to check the issuer id in the assertion, which I did, but I can't identify anything wrong there)

Thanks for the help, I will come back if I have anything new. Except of course if I have overlooked something obvious!

Upvotes: 4

Views: 1778

Answers (2)

JRobinss
JRobinss

Reputation: 201

I have succeeded, so here are some indications for others who may have the same issue.

  1. I hacked simpleSamlPhp because they do not provide the SAML2 assertion (see https://github.com/simplesamlphp/simplesamlphp/issues/220). I did not do it in a nice way, because I only wanted to make it work. It's ugly but it works. Here's my hack, I put it at line 125 of saml2-acs.php:

    // ADDED to get SAML2 assertion in SP
    if (array_key_exists('SAMLResponse', $_POST)) {
        $attributes["samlresp"] = $_POST['SAMLResponse'];
    }
    
  2. In my PHP app, I then use this code:

    $authdata_array= $as->getAuthDataArray();
    $postSaml2Assert = $authdata_array["Attributes"]["samlresp"];
    $msg = base64_decode($postSaml2Assert);
    $document = new DOMDocument();
    $document->loadXML($msg);
    $assertion = $document->getElementsByTagNameNS ("urn:oasis:names:tc:SAML:2.0:assertion", "Assertion")->item(0);
    $saml2AssertionXml = $document->saveXML($assertion);
    
  3. I encode the assertion base64 and URL-encode. For this I used code from Passing base64 encoded strings in URL (thanks joeshmo!)

  4. in APIM / Entity Providers / list / myIdS / edit / Federated Authenticators / SAM2 web SSO... / Identity Provider entity ID I set the value to the expected IDS endpoint, https://myids:9443/samlsso. This solved my initial issue

  5. in the SP configuration (in the IDS) I added the oauth token both as audience and recipient, looks like https://myAPIM:9443/oauth2/token/. Caution, both as audience and recipient!

  6. Because I was debugging I had formatted the XML assertion, so it wasn't working, I ran into the problem detailed here WSO2 Identity Server OAuth2 Bearer SAML Assertion so I simply removed the formatting and used the XML string as-is. (I have of course removed this error from above)

  7. I had a last error easy to solve: the NotOnOrAfter flag was making trouble, because there was a delay between getting the SAML2 assertion and using it to generate a token. Lesson learned: The SAML2 assertion should be created just after logging in, not just before calling APIs.

Hope this helps others. Thanks @Bhathiya for helping!

Upvotes: 2

Bee
Bee

Reputation: 12512

When you login with SAML SSO, you get a SAML response with an Assertion in it. You can see a sample response/assertion here. You need this assertion to obtain an OAuth2 token. However, it looks like your php framework does not provide the SAML response to you directly. That's too bad because you need it here.

Anyway, since you have managed to obtain that, all you have to do now is to base64-URL encode the entire <Assertion>....</Assertion> tag and send with OAuth2 token request. Here is a nice online tool if you need to try manually.

Upvotes: 2

Related Questions