
Solution Overview and sequence
On-Premise Connectivity Plan
of the API Portal service as described in the help page of SAP API Management. After setting it up and creating a service key to get the credentials we're ready to implement the API Proxy and its flow in the API Portal.on-premise-connectivity
instance of the API Management and its service key you'll also need the according permission and entitlement in your subaccount.on-premise-connectivity
instance service key or permission and entitlement to create itAzure AD API Provider
on-premise-connectivity
service instance keyKey Value Map with needed credentials
/token
The actual work then is done in the policies of the API Proxy.PreFlow
of the ProxyEndpoint
to check whether the caller has our shared secret and the token
flow of the ProxyEndpoint
where we do the actual token conversion. The former can be taken from the blog I already linked above and is not part of this blog.Policies in token flow
// extract auth header to retrieve JWT Token
var reqAuth = context.getVariable("request.header.Authorization");
if (!reqAuth) throw 'Missing Auth Header';
var authParts = reqAuth.split(" ");
if (!authParts[0].toLowerCase() === "bearer" || authParts.length !== 2) throw 'Only OAuth Bearer Tokens Accepted'
if (!authParts[1] || !/^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/.test(authParts[1])) throw 'Only valid OAuth Bearer Tokens Accepted'
context.setVariable("private.jwttoken", authParts[1]);
ServiceCallout
policy makes use of the credentials and details of the Azure AD instance we just retrieved from the encrypted key value map. We're using the API Provider which we previously created in the preparation as the HTTPTargetConnection
of our request.<ServiceCallout async="true" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
<Request>
<Set>
<Headers>
<Header name="Authorization">Bearer {private.jwttoken}</Header>
<Header name="Content-Type">application/x-www-form-urlencoded</Header>
<Header name="Accept">*/*</Header>
<Header name="Accept-Encoding">gzip, deflate, br</Header>
</Headers>
<FormParams>
<FormParam name="grant_type">urn:ietf:params:oauth:grant-type:jwt-bearer</FormParam>
<FormParam name="client_id">{private.aad_client_id}</FormParam>
<FormParam name="client_secret">{private.aad_client_secret}</FormParam>
<FormParam name="resource">{private.aad_resource_id}</FormParam>
<FormParam name="requested_token_use">on_behalf_of</FormParam>
<FormParam name="requested_token_type">urn:ietf:params:oauth:token-type:saml2</FormParam>
<FormParam name="assertion">{private.jwttoken}</FormParam>
</FormParams>
<Verb>POST</Verb>
</Set>
</Request>
<!-- the variable into which the response from the external service should be stored -->
<Response>private.azuread.response</Response>
<!-- The time in milliseconds that the Service Callout policy will wait for a response from the target before exiting. Default value is 120000 ms -->
<Timeout>30000</Timeout>
<HTTPTargetConnection>
<APIProvider>INT0201_Azure_AD</APIProvider>
<Path>/{private.aad_tenant_id}/oauth2/token</Path>
</HTTPTargetConnection>
</ServiceCallout>
ExtractVariables
policy.<ExtractVariables async="false" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
<JSONPayload>
<Variable name="private.azuread.access_token" type="string">
<JSONPath>$.access_token</JSONPath>
</Variable>
</JSONPayload>
<Source>private.azuread.response.content</Source>
</ExtractVariables>
ServiceCallout
to the xsuaa instance which will be the last of our service calls. For this we use the third set of variables retrieved from the key value maps: the credentials of our on-premise-connectivity
service instance. For this we use the BasicAuthentication
policy with the client id and secret.<BasicAuthentication async='true' continueOnError='false' enabled='true' xmlns='http://www.sap.com/apimgmt'>
<Operation>Encode</Operation>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<User ref='private.uaa_client_id'></User>
<Password ref='private.uaa_client_secret'></Password>
<AssignTo createNew="true">sapapim.auth</AssignTo>
</BasicAuthentication>
<ServiceCallout async="true" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
<Request>
<Set>
<Headers>
<Header name="Authorization">{sapapim.auth}</Header>
<Header name="Content-Type">application/x-www-form-urlencoded</Header>
<Header name="Accept">*/*</Header>
<Header name="Accept-Encoding">gzip, deflate, br</Header>
</Headers>
<FormParams>
<FormParam name="grant_type">urn:ietf:params:oauth:grant-type:saml2-bearer</FormParam>
<FormParam name="assertion">{private.azuread.access_token}</FormParam>
</FormParams>
<Verb>POST</Verb>
</Set>
</Request>
<Response>sapapim.oauthresponse.token</Response>
<Timeout>30000</Timeout>
<HTTPTargetConnection>
<URL>https://{private.uaa_token_domain}</URL>
<SSLInfo>
<Enabled>true</Enabled>
<ClientAuthEnabled>false</ClientAuthEnabled>
<KeyStore/>
<KeyAlias/>
<TrustStore/>
</SSLInfo>
</HTTPTargetConnection>
</ServiceCallout>
AssignMessage
policy on the response flow of the token flow.<ExtractVariables async="false" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
<JSONPayload>
<Variable name="private.btp.access_token" type="string">
<JSONPath>$.access_token</JSONPath>
</Variable>
</JSONPayload>
<Source>sapapim.oauthresponse.token.content</Source>
</ExtractVariables>
<AssignMessage async="false" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
<Set>
<Payload contentType="application/json" variablePrefix="@" variableSuffix="#">{"auth_token":"@private.btp.access_token#"}</Payload>
</Set>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<AssignTo createNew="false" type="response">response</AssignTo>
</AssignMessage>
ServiceCallout
into any other API Proxy on your instance e.g. like this:<ServiceCallout async="true" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
<Request>
<Set>
<Headers>
<Header name="Authorization">Bearer {request.header.Authorization}</Header>
<Header name="secret">{private.shared.secret}</Header>
</Headers>
<Verb>POST</Verb>
</Set>
</Request>
<Response>sapapim.accessToken</Response>
<Timeout>15000</Timeout>
<LocalTargetConnection>
<Path>/v1/shared/principalpropagation/token</Path>
</LocalTargetConnection>
</ServiceCallout>
Copy
functionality of the AssignMessage
policy to create the request to the Service Callout. /v1/shared/principalpropagation
in this case is the base path of the Shared Principal Propagation API Proxy I created and of course you need to add the resource /token
as well.on-premise-connectivity
service plan, we don't need to dynamically set our target xsuaa instance but can always use the one connected to that instance.You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
16 | |
14 | |
11 | |
11 | |
11 | |
9 | |
9 | |
8 | |
7 | |
6 |