AWS and Cognito - oauth-2.0

I'm a newbie to this stuff so downloaded the samples which is all fine and I thought I could see what was going on and what I needed to do. However, got a bit stuck for no obvious reason so I wondered if anyone could maybe give me some hints.
I'm trying to engineer Cognito authentication and identity into an old Apache Struts 1 legacy web application written in Java, so all the activity needs to be server-side. Using the Cognito https://xxx.auth.xxx.amazoncognito.com/login? URL I can successfully authenticate and get an auth code back using this URL providing my client id, redirect URI and response_type=code so all good thus far.
If I then create an HttpClient (as per the sample code in Github) and call the token URL https://xxx.auth.xxx.amazoncognito.com/oauth2/token and write various parameters to the request body (grant_type=authorization_code, client_id=as previously, redirect_uri=my URI and code=auth code just returned), I get an "unauthorized_client" message returned. But the code is valid albeit for authorization, and the client_id is correct because I used it previously.
My log:
Cognito following successful signin, continuing to url http:[redacted]/passport/CognitoHandlerSignIn.do?code=62eeb0b1-a76b-489b-bd28-e42023a497bd
(this was the /login succeeding)
Callback from Cognito received
(following is the log dump of the /oauth2/token URI called to)
Cognito token signin URL is https:[redacted].amazoncognito.com/oauth2/token
HTTP request header, added Authorization=Basic M29wcGR0azdpYzF2YjloNGd0OTQzNXYxcmI6MW9mMmFsaWNzZGR2dHZ1NmFkOHRuc2s4cnJ0cXEyYm0yc3RqbG1mcmkyamhkdXBubG1wMw==
HTTP request header, added Content-Type=application/x-www-form-urlencoded
HTTP request body, added grant_type=authorization_code
HTTP request body, added
redirect_uri=https%3A%2F%2F<redacted>%2Fpassport%2FCognitoHandlerSignIn.do
HTTP request body, added code=62eeb0b1-a76b-489b-bd28-e42023a497bd
HTTP request body, added client_id=[redacted]
HTTP request is sun.net.www.protocol.https.DelegateHttpsURLConnection:https:
[redacted].auth.eu-west-1.amazoncognito.com/oauth2/token
HTTP Json result=<{"error":"unauthorized_client"}>
org.json.JSONException: JSONObject not found.
at org.json.JSONObject.get(JSONObject.java:454)
at
What's wrong with this picture? I tried also adding client_id, code as URL parameters but I just get an "invalid_client" message instead.
I also tried using the /oauth2/token URI directly from the Struts app to provide a token but it returns the id_token using # rather than ? in the parameter list so it is client-side only and hence can't be intercepted by the Struts app and so will be a pain to forward to the server, but I could write some Javascript to do it if I had to. It doesn't seem the path of least resistance, though, as it seems wrong that the pure Java server side call doesn't work so I must be doing something wrong which isn't obvious to me.

Related

Cannot repeat steps on how to get OAuth2.0 Access Token on Postman

On Postman, I can get new access token for OAuth2.0 by providing callback URL, auth URL and client ID.
I want to break this task down on JMeter because I cannot find this function there. From my understanding, it is divided into authenticate -> authorise -> call back.
Authenticate
URL = https://xxxxx/login
Result = Authorising URL
Authorise
URL = https://xxxxx/oauth/authorize?client_id=mmm&redirect_uri=https://yyyyy/auth/callback&response_type=code
Result = code (e.g. zzz)
Call back
URL = https://yyyyy/auth/callback?code=zzz
Result = token
As I used HTTP(S) Test Script Recorder on JMeter, I got the three actions mentioned above. When I reran them, it told me this error on Authenticate part: <oauth><error_description>Full authentication is required to access this resource</error_description><error>unauthorized</error></oauth>.
To make sure that it was not about the program I use, I did it on Postman and found this error as well.
I wonder how I can break OAuth2.0 Get New Access Token feature into basic API settings in order to get access token on Postman or JMeter.
Dont' compare these tools:
Postman is an Electron application, it's basically a heavily customised Chromium web browser + NodeJS
According to JMeter main page
JMeter is not a browser, it works at protocol level. As far as web-services and remote services are concerned, JMeter looks like a browser (or rather, multiple browsers); however JMeter does not perform all the actions supported by browsers. In particular, JMeter does not execute the Javascript found in HTML pages. Nor does it render the HTML pages as a browser does (it's possible to view the response as HTML etc., but the timings are not included in any samples, and only one sample in one thread is ever displayed at a time).
If you can obtain the token using Postman you can just add HTTP Header Manager to your JMeter Test plan and configure it to send Authorization header with the value of Bearer YOUR_TOKEN_FROM_POSTMAN and JMeter should let you in.
After testing, I found that Postman's OAuth 2.0 Get New Access Token popped up a login page of targeted URL where I needed to fill in the username and the password so that the token could be obtained.
As I tried breaking down APIs required for this login, it required GET of that https://yyyyy and POST of that https://xxxxx/login. Click Send for POST, with username and password contained in form-data, then click Send for GET. The GET response would contain such the token.
However, just putting the aforementioned GET and POST APIs into one thread group did not work on JMeter. As I used HTTP(S) Test Script Recorder to no avail, I went with BlazeMeter and realised that it was using Transaction Controller containing 1) GET of https://yyyyy 2) POST of https://xxxxx/login. With these two arranged top-down, the job would be successful. The token was contained in the response of 2).
For now, this has been my discovery which answers my question.
Try BlazeMeter.

Why does JWT need to be sent as a Bearer Token header?

I am adding JWT Auth for a completely new frontend to a legacy Rails backend.
Upon HTTP request, it seems most sources suggest I send the token back to the server via Bearer Header.
Why? What is the additional value of sending via header (bearer or basic). What can't I simply pass the JWT back to the server via .json and authenticate the token from there.
What benefit does an Authorization header give me, and moreso, what does a Bearer Authorization header give me?
I can of course simply follow everyone's example, but want to understand why. The bearer docs are verbose and hard to understand what I'm gaining over simple sending the JWT as part of the data in the request.
Thank you.
You can technically send a json body on each request with the JTW but that would be non standard behaviour (for instance, GET requests should not have a body via the spec).
A more standard way would be to provide an Authorization HTTP header.
The Authorization header is not specific to JWTs and its role is to specify an auth scheme between the client and the server.
Another way would be to include the JWT inside a cookie but that would make the behaviour browser specific while the HTTP header can be sent by virtually any HTTP client.
P.S
Bear in mind that contrary to Auth cookies which are sent by the browser automatically the Authorization header needs to be set by the client explicitly.

Pocket API Authorization: Forbidden

I'm testing out RoR by building a Rails app with Pocket API, and I have to authorize the user. For HTTP requests, I'm using https://github.com/rest-client/rest-client library.
The first step, obtaining a request token works fine:
require 'rest_client'
response = RestClient.post 'https://getpocket.com/v3/oauth/request', :consumer_key => #consumer_key, :redirect_uri => #redirect_uri
#code = response.split("=")[1]
But I get a Bad Request error on the second step, which is to get an access token using the request token received on the step above:
access_token = RestClient.post 'https://getpocket.com/v3/oauth/authorize', :consumer_key => #consumer_key, :code => #code
400 Bad Request is what I get on Ruby application error screen. I have also tried the same request with cURL and POSTMan Chrome extension, and the status code I get then is: 403 Forbidden. X-Error Code I get is 158 which translates to X-Error message "User rejects code." on Pocket API docs: http://getpocket.com/developer/docs/authentication.
Since I have tried several different channels to test this request and failed each time, I'm guessing that the problem is not with parsing, but rather I might be missing an important detail or a step (maybe HTTP request headers?). Thanks for your help in advance!
Turns out that I (or we) have been missing an important detail:
Whenever testing out your request for Pocket API in POSTMan or anywhere else, we naturally skip the process of visiting the authorization URL which is in the form of:
https://getpocket.com/auth/authorize?request_token=YOUR_REQUEST_TOKEN&redirect_uri=YOUR_REDIRECT_URI
Now, even though you might have allowed your app to access your account before, on each call, Pocket API doesn't activate a request token before this URL is visited. Only then your request token becomes activated and can be used for 2nd authentication step. It works fine after doing that.
As a side note to anyone who is using Pocket API in Ruby on Rails, there is a nice wrapper gem for it: https://github.com/turadg/pocket-ruby
I can confirm that you are indeed missing HTTP headers, which will cause the Pocket server to reject the post request you're trying to send.
There are a few ways in which headers can be communicated: sometimes they are communicated through the codes/tokens associated with the server request (which here appears not to be the case). You need to use an "Authorization" header as per your doing this with OAuth with your initial request.
This should help you: notice the "Authorization:" header after the "Content-Type:" header contains the information that's returned.
For some in depth reading, go here.
I might also suggest trying the OAuth2 gem which does most of the requesting for you - it will probably simplify what you're doing quite a bit!!
Here's what it looks like on Postman.

Why does the Implicit Authorization Grant in OAuth require a "Web-hosted Client Resource"?

I am referring to the Figure 4 of the following link to understand the working behind it:
https://tools.ietf.org/id/draft-ietf-oauth-v2-31.html#grant-implicit
I cannot understand why there's a need for Web-Hosted Client Resource? Why doesn't the User-Agent simply pass the Access Token directly to the client?
Firstly, update your reference of OAuth 2.0 to the latest one.
As we know, in implicit grant,
after resource owner grants access, the authorization server redirects the user-agent back to the client using the redirection URI, and access token is in the fragment.
For example: http://www.myapp.com/googleapi/oauth/#access_token=ya29.JACdaU44_m0MQh0AAABLMVzZHm4KnUWyBECHJ9oM_0M2JC9x0xO6UoI9W8YNEw&token_type=Bearer&expires_in=3600
Since the fragment is not returned to the server (hash fragment is only intended for the client), client-side script must parse the fragment and extract the value of the access_token parameter.
Now, here comes to your questions, of course we can write a function in our client to parse the access token from the fragment and use it directly, it's simple and straightforward. Here is a tutorial using this manner.
But, in the standard, there is Web-Hosted Client Resource. Why?
'Web-Hosted Client Resource' is a client Resource, it may include some html pages and JavaScript, and of course it's web-hosted, rather than in the User-Agent. Since authorization server will hit our web application again at the redirect_uri with access token in fragment, our client-side web application server will respond it(parse the hash fragment).Here is a tutorial using this manner
In a word, the difference between these two manners is where you put the parse function.
Put it directly in the client it self, and the function detects if the user-agent hit the redirect URI, and if so, parses the access token from the fragment.
Put it in web-hosted client resource(locates in the redirect URI), when the authorization server hit the redirect URI with access token, the function locates the redirect URI parse it.
The second one is standard, for it makes the most of the redirect URI, also you can use the 1st one as well.
As far as I know, I haven't found any security consideration about the 2nd way.

No browser is sending Authorization info in header

I'm looking at this and this and it would appear 'easy' to send the credentials in the URL. For example:
http://gooduser:secretpassword#www.example.com/webcallback?foo=bar
This is all well and good but it doesnt work. I've turned fiddler on and for Chrome the Authorization header isnt sent. It appears to exhibit the same behaviour for other browsers (i've got a breakpoint on the server and no Authorize header turns up for Firefox,Safari or IE either)
How to make it better?
Stumbled across this while researching various basic auth implementations.
Browsers generally only send basic auth if they receive a 401 challenge response from the server (more on basic auth protocol). If the endpoint in question accepts both authenticated and non-authenticated users, then a browser-based request will likely never be prompted for the auth parameters.
The easiest way to test this type of setup is to send a curl request (which sends the authentication parameters regardless) to your server endpoint and validate receipt of the authorization header:
curl 'http://gooduser:secretpassword#www.example.com/webcallback?foo=bar'
OK so after much searching and experimenting the approach
http://gooduser:secretpassword#www.example.com/webcallback?foo=bar
does work.
However one needs to be careful to ensure that the secret password DOES NOT contain any special characters. Use a password containing only letters and numbers and a hypen(if you must) and it should work.

Resources