We have the current situation:
In Azure API manager we build some APIs based on a Swagger definition.
The provider of the APIs provided us with a client id and secret.
Some of these API calls need to be authenticated with a bearer token which is generated on the provider's API infrastructure with a /token endpoint mentioned above and we want to integrate the authentication flow for these API calls in APIM (since the frontend will be authenticated in another way (CORS probably))
We tried various approaches using all kinds of variations in "OAuth2.0" service configurations in the APIM setting and apply them to the API definitions by We kept getting Unauthorized 401.
As starting point we used https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad, but most of the the explanations we found concerned using AD, which we don't need as far as we understand.
We tried to implement the following OAuth 2.0 Postman Authorization configuration into APIM (which actually works in Postman).
Is there a simple and straight forward way to tell APIM to do a call to the token URL with a given ClientId and secret and add the authorization header with a bearer token to the backend API?
Thanks to Gary for pointing me in the right direction. I'm quiet new to the topic, so my approach might be far from perfect but it works.
I ended up in modifying the inbound policies of the API call and added the following (replace xxxx's with the appropriate settings)
<policies>
<inbound>
//....
<send-request mode="new" response-variable-name="tokenstate" timeout="20" ignore-error="true">
<set-url>https://xxxxxxxxxx.azurewebsites.net/connect/token</set-url>
<set-method>POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-header name="Accept" exists-action="override">
<value>*.*</value>
</set-header>
<set-body>
#("grant_type=client_credentials&scope=xxxxxx&client_id=xxxxxxxx&client_secret=xx")
</set-body>
</send-request>
<set-variable name="bearerToken" value="#(((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()["access_token"].ToString())" />
<set-header name="Authorization" exists-action="override">
<value>#("Bearer " + (string)context.Variables["bearerToken"])</value>
</set-header>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-header name="Accept" exists-action="override">
<value>*/*</value>
</set-header>
</inbound>
Short explanantion
A new request is initaited which response will be stored in the variable (token state)
The method is defined as POST
Headers for the request are set (Centent-Type & Accept)
The body of the request is defined
Since the response of the token request (stored in tokenstate) is JSON formatted, the response of the request is cast to a JObject and the "access_token" is stored in the "bearerToken" variable (alternatively you could do without assigning the variable and put this line immediately in the next step.
Set the "Autorization" header with the value "Bearer " + [bearerToken]
the additional steps (Set header Content-Type & Accept) I needed to be able to debug, but in normal cases they will be added by the requesting client of the API.
Yes - you can do this and here is a Curity resource that follows a similar process:
Make an OAuth request to get a JWT based on an incoming credential
Forward it to the downstream API
Cache the result for subsequent requests with the same incoming credential
Your case is a little different but uses the same building blocks. You just need to adapt the OAuth message to use the Client Credentials flow.
Related
I am recently working with Uipath Orchestrator API and I see that they use Bearer token to authenticate. But I am confused and I do not know if this way of authentication is oAuth or it can just be called Bearer Authentication.
So for API authentication, it depends on what orchestrator you are using.
On-Premise
If you are using an on-premise installation of Orchestrator you would send the following JSON body to your Yourorchestrator.com/api/Account/Authenticate
{
"tenancyName":"default",
"usernameOrEmailAddress":"your username",
"password":"your password"
}
you will get a JSON string returned and you want to extract the value for key result this is your authentication token.
Now that you are authenticated you can send your requests with the following header
Name - Authorization
Value - Bearer + your token from above
This is your authentication done.
Cloud
If you are using UiPath cloud instance of orchestrator you will need to send the following JSON body
{
"grant_type" : "refresh_token"
"client_id" : "ClientId"
"refresh_token" : "UserKey"
}
ClientId and UserKey are generated through the cloud instance of Orchestrator
You must also send the following header along with the JSON Body
Name - X-UIPATH-TenantName
Value - Your tenant logical name from orchestrator
Similar to the on-site orchestrator, this will return an authentication token, however, you will need to extract the value for key access_token.
To use this, you send the request with the following 2 headers
Name - Authorization
Value - Bearer + your token from above
Name - X-UIPATH-TenantName
Value - Your tenant logical name from orchestrator
There are some quite useful examples at https://postman.uipath.rocks/?version=latest
It can be quite fiddly to start with, and on-prem Orchestrator is definitely easier to connect with than Cloud orchestrator. I would definitely recommend using Postman to build your API calls
I am creating a dashboard extension, and would like to call Executionhistory - List.
Currently, the extension manifest specifies only the scope vso.serviceendpoint_manage.
I get my authorization token via
const token = await VSS.getAccessToken();
var authHeader = VSS_Auth_Service.authTokenManager.getAuthorizationHeader(token);
I am able to successfully get a list of all service endpoints by using Endpoints - Get Service Endpoints
However, when I try to get the Execution History for a given Service Endpoint, I get a CORS policy error.
Access to {devops rest api} from origin {marketplace url} has been blocked by CORS policy
I know CORS errors can occur if the PAT does not have the correct scopes see here.
Note: When I try this locally with a PAT with all scopes, this call works. So I am fairly certain I am missing an extension scope.
Which scope am I missing?
To get service endpoint execution history, no need to set anything other, just set PAT token scope as Read & manage to Tokens:
You will see it is work to run this Executionhistory - List api.
When attempting to perform an HTTP post via Swagger (using the Swagger Type Provider) I get a 401 Unauthorized.
I suspect the answer to this might be UseDefaultCredentials which isn't exposed when inheriting from the SwaggerProvider.Internal.ProvidedSwaggerBaseType. So I was thinking that an upcast :> might help but posts I've seen elsewhere indicate naïveté.
Also, updating the config for fsi.exe to include the following proved wishful:
<system.net>
<defaultProxy enabled="true" useDefaultCredentials="true">
<proxy usesystemdefault="True" />
</defaultProxy>
</system.net>
Might anyone have a simple answer?
Thanks
Schema access
SwaggerProvider assumes that Swagger schema is easy-accessible and can be downloaded using simple Http request without authentication
type PetStore = SwaggerProvider<"http://petstore.swagger.io/v2/swagger.json">
user also can add any HTTP header to schema-request like this
type PetStore = SwaggerProvider<"http://petstore.swagger.io/v2/swagger.json", "Content-Type=application/json">
It can be Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l header, but this literal string will be hard-coded in source code.
If schema-request requires more complex authentication, will be easier to download it and put it near source code
[<Literal>]
let schemaPath = __SOURCE_DIRECTORY__ + "/PetStore.Swagger.json"
type PetStore = SwaggerProvider<schemaPath>
Requests control
When schema are in place you have a full control over all HTTP request to the server using CustomizeHttpRequest parameter in provided type.
For example, if you want to use default credentials:
let store =
PetStore(
CustomizeHttpRequest=
fun (req:System.Net.HttpWebRequest) ->
req.UseDefaultCredentials <- true
req)
You are free to modify web request as you need:
Use default credentials
Specify runtime Credentials
Add headers to HTTP request
Add cookies to HTTP request
Break request ;) and etc.
CustomizeHttpRequest will be called in the run-time for each request, after SwaggerProvider built it and before call to the server. So you can change anything you want.
Postman has Authentication helpers to help with authenticated calls and I'm trying to use the OAuth 2.0 helper to call a REST server created by JHipster using Spring (Security, Social, etc).
I've tried a lot of configurations, this is the screen (client ID and Secret were masked):
For the Authorization URL I've tried:
http://127.0.0.1:8080/oauth/authorize
http://127.0.0.1:8080/#/login (the app's login route)
The closer I get from receiving a token back to Postman is:
I don't know why it's erring like this. Maybe I'm setting the Callback URL incorrectly? Do I need to do this in the server or in the client (AngularJS)?
Does anyone have any idea of what's wrong? I appreciate your help.
JHipster is currently setup to use the "password" oauth2 grant type. The helper oauth2 helper only seems to work with "authorization code" and "client credentials" grant types.
What you'll want to do is first call your app's token endpoint directly as the angular app does in
src/main/webapp/scripts/components/auth/provider/auth.oauth2.service.js
POST http://localhost:8080/oauth/token?username=MY_USERNAME&password=MY_PASSWORD&grant_type=password&scope=read%20write
where your username and password can be "user" and "user" respectively, for example and with one header set:
Authorization: Basic AAAAAA
where AAAAAA is your (clientId + ":" + clientSecret)--all base64-encoded. You can use https://www.base64encode.org/. For example if your clientId is "jhipsterapp" and your clientSecret is "mySecretOAuthSecret", replace AAAAAA with "amhpcHN0ZXJhcHA6bXlTZWNyZXRPQXV0aFNlY3JldA==" since that is "jhipsterapp:mySecretOAuthSecret" base64-encoded.
That should return you an access_token. Now hit your API endpoints by calling them with the access_token from your password request in your header like this.
Authorization: Bearer access_token_from_earlier_token_request
Update: if you're using microservices and UAA, then see Niel's answer https://stackoverflow.com/a/45549789/1098564
To build on #sdoxsee's answer:
Currently (August 2017) JHipster generates a class called UaaConfiguration with the configure(ClientDetailsServiceConfigurer) method setting up the client ID, client secret, scope and grant type. Refer to these settings (including the referenced JHipster properties in the application*.yml) to populate the Postman authentication helper, using /oauth/token as both Auth URL and Access Token URL.
Example:
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
/*
For a better client design, this should be done by a ClientDetailsService (similar to UserDetailsService).
*/
clients.inMemory()
.withClient("web_app")
.scopes("openid")
.autoApprove(true)
.authorizedGrantTypes("implicit", "refresh_token", "password", "authorization_code")
.and()
.withClient(jHipsterProperties.getSecurity().getClientAuthorization().getClientId())
.secret(jHipsterProperties.getSecurity().getClientAuthorization().getClientSecret())
.scopes("web-app")
.autoApprove(true)
.authorizedGrantTypes("client_credentials");
}
And,
jhipster:
security:
client-authorization:
client-id: internal
client-secret: internal
Means your authentication helper should be populated as follows:
I've a problem when trying to do a webrequest to UGC and authenticate using oAuth. I'm making a webrequest such as:-
WebRequest wr = WebRequest.Create("http://ugc.service/odata.svc/Ratings(Id=200)");
wr.Headers["authorization"] = "OAuth " + auth;
Where auth is my token returned from the access_token.svc. According to the documentation the token returned from the service should be something like:-
HufXeuUt%2FYYElA8SYjJOkUkrXxV9dyXRirmKhjW%2Fb%2FU%3D
However, what I'm being returned from access_token.svc is more like:-
{"access_token":"client_id%3dtestuser%26expiresOn%3d1361898714646%26digest%3d%2fW%2fvyhQneZHrm1aGhwOlgLtA9xGWd77hkxWbjmindtM%3d","expires_in":300}
I've parsed the JSON to extract various strings and attempted to pass these through to the authorization but whatever I try I get an error in the logs - "ERROR OAuth2AccessToken - Digest is wrong." Exactly what part of the token and in what format should I be passing through to authorization?
Many thanks
John
Like you mentioned, the protocol is this:
You make a post request to the access token end-point to get a token (you need to provide here your client_id and your client_secret as headers or as query parameters);
You get an answer similar to this: {"access_token":"sometoken","expires_in":300};
2.1 Worth knowing is that the token is url encoded and in UTF-8 format so, on Java side you need to do URLDecoder.decode("sometoken", "UTF-8"); while on .NET side you need to do HttpUtility.UrlDecode("sometoken", System.Text.Encoding.UTF8);;
Your next request needs to include the authorization header. On Java side you do builder.header("authorization", "OAuth " + decodedTokenString); while on .NET side you can use Client.Headers["authorization"] = "OAuth " + DecodedTokenString;
Worth mentioning is that the SharedSecret defined in the cd_webservice_conf.xml (/Configuration/AuthenticationServer/SharedSecret/) of the TokenAccessPoint needs to be the same as the SharedSecret defined in the cd_ambient_conf.xml (/Configuration/Security/SharedSecret/) of the (WebService)EndPoint.
Are you sure you decoded properly the token gotten from the server? Are you sure that you configured the proper SharedSecret in the two configuration files?
Hope this helps.