I am using Identity Server 3 for implementing OAuth2 authentication.
I want to unit test the authorization code flow.
I am able to make a GET request to the authorize endpoint and get back a signin value.
I would like to authenticate the user to get the authorization code back and then use the code to get the access token.
I need some help with authenticating the user using the login link provided in a unit test.
I'm not sure if you have the same situation as I do, but the steps I needed to do are;
GET from /connect/authorize supplying made-up nonce and state. From the result you need to gather the signin param and fish out the anti-forgery token from the HTML. Also note that there should be a couple of cookies, sign-in and another anti-forgery token. DO NOT mess with those, keep them
POST to /login with the signin param and form fields of username, password and anti-forgery token AND send in the cookies.
Gather the session from the resulting cookies OR check the HTML for an error message
Related
So I've been trying to use Djoser JWT and I don't know how to login in it. As far as I know you create a request with your login credentials to this url /jwt/create/ and get the access and refresh token and maybe get the user object from /users/me/. This is where I got stuck, where do I go from here?
You correctly understood the first step of the process. Basically, you can now:
Add the access token in the header of your next requests.
This will transparently authenticate the user thanks to the Simple JWT plugin and you will be able to access him with the usual request.user in the views.
Refresh the access token each time you get a 401 response.
The access token is supposed to be short-living for security concerns and a 401 response from the server indicates that the one your are using is expired. So you have to get a new one by sending the refresh token to the token/refresh/ API and, then, make your request again.
You can read this article if you need more detailed explanations about this process or JWT.
I am using django version 3.2.4 in combination with Django Rest Framework.
I also use https://github.com/jazzband/djangorestframework-simplejwt for JWTs.
Problem:
I need to enable Two Factor Authentication in the following way.
User uses an endpoint that he submits his username & password. He receives back a jwt token that includes in a claim the verification code he needs to enter hashed.
At the same time an email with this code goes to his email.
Once the user receives the email he posts in another enpoint the verification code received and the jwt token he received in the previous step.
If the code the user submitted matches the hashed code then he obtains the jwt token that it will be used in the subsequent requests.
P.S. Any other ideas, that achieve something similar with the above are welcomed.
The way I would approach this is by adding a confirmation_code field to user model and handle login using one view with two cases:
1st case, I have username and password in request but no code, after checking username and password, I would create manually and return a short lived access token only without refresh token (which would expire in 3 minutes for example), I'd create a confirmation code, save it in db and send it via mail or sms etc...
2nd case the user posts the received confirmation code using the access token in the request, but no username and password, this way I'd be sure the user has an available access token and confirmation code, I would then return access token and refresh token as usual, in this step I would blacklist the access token after login is confirmed.
When using Postman to fetch an access token via Authorization Code, one of the fields I need to enter is for the Callback URL, aka the redirect URI query param when it's making the request to the authorization endpoint. I understand this URL needs to be registered/whitelisted within the OAuth provider, but my question is how does postman actually handle/intercept that request/redirect back when it's localhost-based? For example, if I already had a local server running on http://locahost:8090, and I told postman to use http://localhost:8090 for that callback, how does Postman end up seeing that request/redirect back (to exchange the auth code for an access token) instead of my local web server handling that request?
TL;DR: Postman basically ignores the callback URL when processing the response.
The Long Story
It does need it, but only for the request. As you say, it needs to be correct - exactly matching the IdP client application config - but that's it.
Postman is just helping you acquire the token, it doesn't need to provide it to the consuming application, which is the whole point of the redirect URL - a static path known by the client app and the OAuth client application that makes sure an evil website / intermediary doesn't steal tokens by abusing the redirection flows.
Since it's not meant to work on a browser on the internet, Postman can ignore the redirect. Once the IdP responds with the token then, as far as Postman is concerned, it's good to go. It can save the token in the local token store and use it to make API requests.
Implicit Flow
This is set up to get a token from an Okta endpoint:
When I click "Request token", Postman makes a request like this:
GET https://exampleendpoint.okta.com/oauth2/default/v1/authorize?nonce=heythere&response_type=token&state=state&client_id={the_client_id}&scope=profile%20openid&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fimplicit%2Fcallback
Postman pops a browser to make this request to the /authorize endpoint, at which the IdP then either creates the token (if the browser already has a cookie), or performs various redirects to authenticate the user and then create the token.
At the end of this flow, Postman will receive the 302 from the IdP that contains the token (on the location header). The target of that redirect is the redirect URL configured in the IdP:
302
Location: http://localhost:8080/implicit/callback#access_token=eyJraWQiOiJxOGRmTGczTERCX3BEVmk4YVBrd3JBc3AtMFU1cjB6cXRUMFJveFZRUVVjIiwiYWxnIjoiUlMyNTYifQ.{the_rest_of_the_token}&token_type=Bearer&expires_in=3600&scope=profile+openid&state=state
At this point Postman grabs the token from the #access_token parameter and it's good to go.
Auth Code Flow
Auth Code flow comes in 2 flavours:
Auth Code (Classic)
Auth Code + PKCE
Auth Code flow has been seen as "better" than the implicit flow because it requires a 2nd step in the process to get an access token. You hit authorize which gives the client a code and the code is then exchanged for the tokens. This code for token gives more chances for the server side components to do more stuff - extra checks, enrich tokens and various other things.
Q: Why are there 2 Auth Code flows?
A: The problem with this was that it required a server side component, which many SPA's and/or mobile apps didn't want to host. The endpoint that receives the code and gets the token(s) had to maintain credentials - a client id and client secret - which are required by the IdP when creating the token. PKCE is an extension that removes the requirement for a trusted server. It's adds computed hash to the /authorize call, which the IdP remembers, and then on the subsequent call to /token the client provides the source value of the hash. The server does the same computation, checks it's the same as that on the original request and is then satisfied that it's not handing out tokens to a bad guy.
Auth Code with PKCE
In terms of redirects, this is exactly the same as implicit. But for requests, it needs to make the second request to exchange the code for the tokens. The main differences here are
the access token URL, which is where to send the code and get tokens in response.
the code challenge and verifier, which are PKCE requirements for generating and computing the hash
The requests are now as follows:
The GET to /authorize
GET https://exampleendpoint.okta.com/oauth2/default/v1/authorize?nonce=heythere&response_type=code&state=state&client_id={client_id}&scope=profile%20openid&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fimplicit%2Fcallback&code_challenge=E7YtiHqJRuALiNL_Oc5MAtk5cesNh_mFkyaOge86KXg&code_challenge_method=S256
Postman will pop the browser (and the IdP will redirect it through login if required)
The eventual code response is also 302, however the location header contains the code rather than tokens:
location: http://localhost:8080/implicit/callback?code=J3RlQqW122Bnnfm6W7uK&state=state
So now the client needs to call the endpoint defined in the "Access Token URL" field to get tokens:
POST https://exampleendpoint.okta.com/oauth2/default/v1/token
Body:
grant_type: "authorization_code"
code: "J3RlQqW122Bnnfm6W7uK"
redirect_uri: "http://localhost:8080/implicit/callback"
code_verifier: "Fqu4tQwH6bBh_oLKE2zr0ijArUT1pfm1YwmKpg_MYqc"
client_id: "{client_id}"
client_secret: ""
And the response is a good old 200 that doesn't redirect - the authorize call sends the client back to the final redirect landing page, and the POST is just a normal request with the tokens on the the response
{"token_type":"Bearer","expires_in":3600,"access_token":"eyJraWQiOiJxOGRmTGczTERCX3BEVmk4YVBrd3JBc3AtMFU1cjB6cXRUMFJveFZRUVVjIiwiYWxnIjoiUlMyNTYifQ.*******","scope":"profile openid","id_token":"eyJraWQiOiJxOGRmTGczTERCX3BEVmk4YVBrd3JBc3AtMFU1cjB6cXRUMFJveFZRUVVjIiwiYWxnIjoiUlMyNTYifQ.********"}
I have implemented an endpoint for login, using the django-rest-framework and TokenAuthentication. What i want, is after the login, the user to be authenticated and can navigate on the website.
I know that any get request on any protected using authentication uri should contain the token in the headers, so that the user can be authenticated. Everything is fine with that, if i could do all the get requests adding the token manually.
But what i do not understand is, how can i add the token in the headers when for example the user manually does the request by writing the url?
Let's say that the uri /api/ is protected and requires an authenticated user.
The user logs in, and i save the token either on the cookies or in the localstorage.
Now the user does a http get request on /api/. The token is not placed in the headers, so the response is: "Not authenticated".
So the question is, how can i add the token on any subsequent request after user logs in successfully? Maybe the backend could check the cookies for a valid token, but isn't there any better and safer solution than this?
As I believe from the question you want to add the token to all API which is consumed by your client whether App/Web. So in both people prefer to store that token either in cookies or in local storage. Once user logged out api consumer also flush that key.
I would like to use django-rest-framework token to authenticate users. My workflow would be:
User requests a page
If auth token is present, respond with the requested data.
If auth token is not present, redirect to the login page (with the request page).
Inside the login page, user submit their credentials
If credentials were correctly authenticated, get or create a token for that user and redirect back to the requested page with the token.
Else, respond with error.
Lastly,
When the user logs out, delete the token for that user.
So my question is, is it okay to delete and create a new token for every login if the user has already logged out? Also I assume the token will be unique, am I correct? Your help and guidance is very much appreciated. Thank you.
A REST API should be stateless, that means that there should not be a "session" hence no login and no logout, and no redirections to a login page.
If the request doesn't have a token then the API should return (probably) a 401 Unauthorized HTTP status code and not a redirection. You're making an API so there won't be human interaction. Django rest framework offers a human-friendly interface that does have sessions, login/logout, and if that's all you need the go for it, you can do whatever you want. But It'd be hard for another program to use your API.
why not using tokens with expiration dates or using another well known authentication method ?? :P
Hope this helps :)