Retrieve end user information from IdP in a sequence - wso2

There is a legacy Rest service which has its own authentication architecture (self-owned db to store user information and using basic auth). Other rest services are all using Keycloak as IdP (with OpenID as token).
I'm trying to use WSO2 API manager before the legacy Rest API that it can authenticate calls using OpenID just as other api services. I found there's a guide saying how to pass the end user attributes to the back-end service and also how to pass a custom authorization token to the back-end service.
And now, I want to know whether it is possible to retrieve the caller's id (viz., the end user id) in a sequence or not? It means I can get the end user's id from a property, modify it and then set it to the Authorization header.
Thus, the complete process seems as:
An end user generates a JWT token from Keycloak
The end user calls an api published by API Manager with the token
API Manager retrieves the end user's userid and sets it into the Authorization header
API Manager calls the back-end service with the changed Authorization header

You can try implementing the following sequence as an in-sequence to your API. The following sequence retrieves the End User ID using the Synapse Properties and assigns it to the Authorization Header.
<?xml version="1.0" encoding="UTF-8"?><sequence xmlns="http://ws.apache.org/ns/synapse" name="username-header">
<property name="userid" expression="$ctx:api.ut.userName"/>
<!-- property name="userid" expression="$ctx:api.ut.userId"/ -->
<log level="custom">
<property name="User ID" expression="$ctx:userid" />
</log>
<property name="Authorization" expression="$ctx:userid" scope="transport" action="set" />
</sequence>
Furthermore, you can also try using the following Synapse properties, if the above doesn't fulfil your requirement.
api.ut.username
api.ut.userId
If you are trying to read the end user's ID from the Backend JWT token that is generated by the API Manager during the API invocation, then it is required to come up with an extended mediation sequence, to read and decode the Token, and then to extract the information. Given below is a sample (not complete) if the requirement is as described above
<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="admin--AssertionAPI:v1.0.0--In">
<property name="assertion" expression="$trp:X-JWT-Assertion" />
<log level="custom">
<property name="Assertion" expression="$trp:X-JWT-Assertion" />
</log>
<property name="Body" expression="base64Decode(fn:substring-before(fn:substring-after($ctx:assertion, '.'), '.'))" />
<log level="custom">
<property name="body" expression="$ctx:Body" />
</log>
<!-- use script mediator to access required properties -->
<!-- and set the value as a header -->
</sequence>

Related

WSO2 Decode JWT

I have a structure with APIM, IS and Microintegrator. Configured APIM to pass attributes to backend via X-JWT-Assertion header.
Now I need to decode this JWT into Json format to use some claims.
I tried it with the function:
<property expression="base64Decode(get-property('JWT_HEADER'))" name="decoded"/>
but it returns the scrambled data.
enter image description here
Does anyone know how I can do decoding?
Thanks.
A JWT token contains three parts separated by dots (.) as follows [JWT.io]
Header
Body
Signature
If you are trying to access the claims, you have to split the body part and then decode it. Otherwise, it will result in an unreadable format as you have mentioned above.
The following is a sample expression to split and decode the Body of the JWT Token
<property name="assertion" expression="$trp:X-JWT-Assertion" />
<!-- split the body content using substring functions -->
<property name="decoded" expression="base64Decode(fn:substring-before(fn:substring-after($ctx:assertion, '.'), '.'))" />
<log level="custom">
<property name="decoded-body" expression="$ctx:decoded" />
</log>

How to publish WSO2 API on WSO2?

I have a special requirement.
I want to access WSO2 backen API.
So,I want to publish it on WSO2.
So that,my service can access WSO2'API.
Can someone tell me how to do it?
WSO2 API Manager exposes a REST API to for operations like publish and subscribe. You can see the list of all the services here: https://docs.wso2.com/display/AM200/Published+APIs.
It also offers Swagger definition files for those same APIs: https://raw.githubusercontent.com/wso2/carbon-apimgt/v6.0.4/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher/src/main/resources/publisher-api.yaml.
I haven't tried it, but given the fact that all you need to publish a managed API in WSO2 is a Swagger file and an existing endpoint, there is nothing preventing you to create an API for WSO2 APIM inside itself.
Theoretically, this should be fine.
WSO2 API manger plays the middleware role for its own APIs just as for any other third party API.
Let's say that you want to publish any of the Admin APIs of the api manager through the api manager itself. You just need to add endpoint information just as you would do for any other api.
Then, once a user wants to access the api, they have to first get a access token by calling the token endpoint, then use that token to execute the published api.
Access you API-Manager on: http://localhost:9443/publisher
Click on your api
Click on tab "Lifecycle"
Click on "Publish"
WS02 API means are you referring to Wso2 API manager?
Because you can also implement API's using Wso2 ESB.
You can have more control over the code of API using Wso2 ESB but you cannot have throttling and other control using ESB you have to use APIM.
You shoould have an endpoint to connect your API.
Then follow the below steps
Login in your API Manager (Publisher) Instance.
+ Add new API
If you have an endpoint: Click on Design a new Rest API
Fill the values like name, context, version and description.
Add a new API Specification. (i.e: GET /test)
Click on Next:Implement
Select Managed API
Endpoint Type: HTTP/REST Endpoint
Endpoint: Fill your endpoint's url without context.
Click on Next: Manage
Fill values like type of subscription.
If you have a public API, don't forget change the second column of your API specification resources. Change the value for: None
Click on Save & Publish
Go to WSO2 Store and check
While going through the answers and the replies that you have provided, I believe you know how to publish an API with a WSO2 API as an endpoint. However, your concern is how to handle the authorization since the backend itself needs a valid OAuth token to be invoked? Please correct me if my understanding is wrong.
If that is the case, I believe you can simply achieve it with a help of a custom mediation. The below sample mediation is to invoke the token endpoint and generate a token. Once the token is generated you can simply add that to the request header.
<?xml version="1.0" encoding="UTF-8"?>
<sequence name="simple-token-gen" trace="disable" xmlns="http://ws.apache.org/ns/synapse">
<property description="access_token" expression="get-property('registry', 'local:/api-backend-credentials/pizzaOrderingAPI/access_Token')" name="access_token" scope="default" type="STRING"/>
<property description="generated_time" expression="get-property('registry','local:/api-backend-credentials/pizzaOrderingAPI/generated_Time')" name="generated-time" scope="default" type="LONG"/>
<property description="client_credentials" name="app-client-auth" scope="default" type="STRING" value="{base64encoded(clientKey:clientSecret)}"/>
<property expression="json-eval($)" name="message-body" scope="default" type="STRING"/>
<property expression="get-property('axis2','REST_URL_POSTFIX')" name="resource" scope="default" type="STRING"/>
<filter description="" xpath="get-property('SYSTEM_TIME') - get-property('generated-time') > 3600000 or get-property('access_token') = ''">
<then>
<payloadFactory media-type="xml">
<format>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<root xmlns="">
<grant_type>client_credentials</grant_type>
</root>
</soapenv:Body>
</soapenv:Envelope>
</format>
<args/>
</payloadFactory>
<header expression="fn:concat('Basic ', get-property('app-client-auth'))" name="Authorization" scope="transport"/>
<header name="Content-Type" scope="transport" value="application/x-www-form-urlencoded"/>
<property description="messageType" name="messageType" scope="axis2" type="STRING" value="application/x-www-form-urlencoded"/>
<property description="REST_URL_POSTFIx" name="REST_URL_POSTFIX" scope="axis2" type="STRING" value=""/>
<call blocking="true">
<endpoint name="token">
<http method="post" uri-template="{token-endpoint-url}"/>
</endpoint>
</call>
<property expression="get-property('resource')" name="REST_URL_POSTFIX" scope="axis2" type="STRING"/>
<property description="generated Time Setter" expression="get-property('SYSTEM_TIME')" name="local:/api-backend-credentials/pizzaOrderingAPI/generated_Time" scope="registry" type="LONG"/>
<property description="generated_token" expression="json-eval($.access_token)" name="generated-access-token" scope="default" type="STRING"/>
<property description="new Token setter" expression="get-property('generated-access-token')" name="local:/api-backend-credentials/pizzaOrderingAPI/access_Token" scope="registry" type="STRING"/>
<header expression="fn:concat('Bearer ', get-property('generated-access-token'))" name="Authorization" scope="transport"/>
<payloadFactory media-type="json">
<format>
$1
</format>
<args>
<arg evaluator="xml" expression="get-property('message-body')"/>
</args>
</payloadFactory>
</then>
<else>
<header expression="fn:concat('Bearer ', get-property('access_token'))" name="Authorization" scope="transport"/>
</else>
</filter>
</sequence>
Replace the placeholders with corresponding values.
{base64encoded(clientKey:clientSecret)} - The client key and client
secret, separated by a colon and base64 encoded. {token-endpoint-url}
The token endpoint URL of the backend authorization server.
I believe this helps you with your concern.
If I understood correctly, It has no sense to publish WSO2 AM APIs in the API Manager. First reason is that WSO2 AM APIs are accesed by Basic Auth, that it is not implemented by default in WSO2 AM. The default security for APIs en WSO2 is OAuth2, so you would have to do a extension for that. You may check here: https://docs.wso2.com/display/AM260/apidocs/publisher/#guide
Also, these internal APIs for WSO2 AM are meant to be accesed by an admin or so, so it has no sense to expose these as an API to be consumed for other suscriptors.
Anyway, if you implemented an authenticator to run with Basic Auth, you may expose this APIs in API Manager, or expose these APIs disabling default OAuth2 security.
BR

How to retrieve HTTP REST METHOD of current service at Run time in WSO2 AM Sequence?

How to retrieve HTTP REST METHOD(GET,PUT,POST,DELETE,OPTIONS) in WSO2 Api Manager's Sequence at runtime? I tried to $ctx:REST_METHOD which returns 'null' value.
<sequence name="ec_rest_dynamic_ep" trace="disable" xmlns="http://ws.apache.org/ns/synapse">
<property expression="$ctx:REST_METHOD" name="restmethod"
scope="default" type="STRING"/>
<log>
<property expression="get-property('restmethod')" name="*******************REST_METHOD***********"/>
</log>
</sequence>
Basically, HTTP REST METHOD value of current service & URL context of that service needed to identify the service in order redirect the service to its endpoint dynamically at runtime.
Try the following property.
<property name="Http_Method" expression="get-property('axis2', 'HTTP_METHOD')"/>
You can find more useful properties in [1].
#Pubci's answer is correct. Here is another way.
<property name="Method" scope="transport" expression="$ctx:api.ut.HTTP_METHOD"/>
Some other properties can be found here.

Passing along application information from WSO2 APIM to specific API's

I'm looking for a way to communicate the user and the subscribed application name from the WSO2 API Manager to specific API's. I already found documentation on using JWT for this, but that would imply that all connected API's get this information, something that is not desirable in our scenario.
Can the same be accomplished using some sort of mediation sequence? I can't find any proper documentation on what parameters and fields are available there.
Thanks in advance.
Yes you can write a custom mediation sequence like this.
<sequence xmlns="http://ws.apache.org/ns/synapse" name="admin--Temp:v1.0.0--In">
<property name="UserName" scope="transport" expression="$ctx:api.ut.userName"/>
<property name="AppName" scope="transport" expression="$ctx:api.ut.application.name"/>
<log level="custom">
<property name="User" expression="$trp:UserName"/>
<property name="App" expression="$trp:AppName"/>
</log>
</sequence>
Here I'm creating 2 transport headers with user name and app name.

WSO2 API manager prototype API HTTP response status

I am using the inline javascript prototype feature in the WSO2 API manager and I'm trying to set different HTTP response statuses. Is this possible? If so how is it done?
So far I have tried setting the HTTP_SC property but this doesn't seem to have any effect.
mc.setProperty('HTTP_SC', "404");
I had the same requirement and after much exploring under the hood was able to find a workable solution.
The reason why setting the property:
mc.setProperty('HTTP_SC', "404");
didn't work is that the property needs to be set in the axis2 scope (as Abimaran said). mc.setProperty doesn't set it on that scope. Moreover, the MessageContext object doesn't provide a way to set the scope.
The 'Deploy as Prototype' action actually creates the API definition file by merging the specified in-line script into the a velocity template and storing the resulting API definition into a file.
Template: ./repository/resources/api_templates/prototype_template.xml
Output location: repository/deployment/server/synapse-configs/default/api/
The output file will have a name in the format:
provider--API Name-vVERSION.xml
where provider appears to be the username of the API creator.
What I did was add a filter to the template:
<filter source="boolean(get-property('HTTP_SC'))" regex="false">
<then>
<property name="HTTP_SC" value="200" scope="axis2"/>
</then>
<else>
<property name="HTTP_SC" expression="get-property('HTTP_SC')" scope="axis2"/>
</else>
</filter>
I added it immediately after a similar block (for handling CONTENT_TYPE) at the start of the inSequence element.
You need to add following properties before <send/> mediator
<header name="To" action="remove"/>
<property name="RESPONSE" value="true"/>
<property name="HTTP_SC" value="403" scope="axis2"/>