I'm looking to propagate a JWT token between my services running in docker using the library flask-jwt-extended and I have an idea of how I would do this using something similar to this:
request.post(url, json={"access_token": access_token, "refresh_token": refresh_token)
But in my experience I need to return a response to do this.
I already have the frontend creating tokens and protecting my routes. I just want to use that token to do the same for the backend.
I want to be able to login from my frontend application and when I login that propagates the token throughout the other services. How do I approach this?
I will send the post request to a function that will look something similar to this:
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == "POST":
resp = jsonify({'login': True})
set_access_cookies(resp, request.json["access_token"])
set_refresh_cookies(resp, request.json["refresh_token"])
return resp, 200
Do I need to return that response?
Token sharing should be accomplished via signature trust. Make sure that your other services "know" the public key of the trusted signer.
Here's the basics:
Frontend requests token from backend via authorization api
Backend validates credentials, issues token using 'RSXXX' algorithm, eg. 'RS512'
Frontend passes token to all calls to any of your backend services.
When backend receives a token it verifies signature and "source" using the public key identity of the token before applying token payload to the requested operation.
All backend services and the frontend should have a configuration element which defines one or more trusted public keys used for token signing.
This article has some helpful information on using a public/private key pair with pyjwt:
https://blog.miguelgrinberg.com/post/json-web-tokens-with-public-key-signatures
Related
I'm building a FastAPI application with OAuth2 and JWT authentication. I've got two endpoints that create the JWT token. The first is hidden from the OpenAPI page but is used by the page Authorize button. The second does the same functionality but is available to the users as an API endpoint.
If the user uses the page Authorize button and successfully gets authenticated, the rest of the API endpoints on the OpenAPI page become accessible.
If the user uses the API get_token endpoint only, they get a valid JWT token, which can be used with the protected API's, but the OpenAPI page isn't authenticated.
How can I use the token returned by the public get_token API endpoint to authenticate the OpenAPI page as if the user went through OpenAPI provided Authorize functionality?
When using the Authorize button, the Authorization header with the token in it is automatically sent in every subsequent request you make to the FastAPI backend, and hence, the user gets authenticated.
Using your get_token endpoint, users will obtain the token as a response, and have to manually place it in the headers for every request they make. As described in this answer, as Authorization is a reserved header in Swagger UI/OpenAPI specification, you either have to define a Header parameter in your endpoints with a different name, e.g., token (where users will place the token value), or use the Authorize button, which will automatically add it for every request to any endpoint of your server.
Another option would be to create an httponly cookie (using the Set-Cookie header) with the token inside once the user calls the get_token endpoint, and return the cookie with the Response object, as described here and here. The browser will store the cookie sent by the server and will send it back with every request made to the server inside a Cookie HTTP header (read more about cookies here). Inside the endpoint, you can then retrieve the token using the Request object directly, for example, request.cookies.get('token'), or by defining a cookie parameter for it.
I have two separate Flask applications, one is an API with the domain "admin.company.com" and the second one is a dashboard under the domain "dashboard.company.com".
My main goal is to secure the api and enable authentication.
I set up authentication on the api and when I'm testing it via the swagger-ui it works good with no issues. I manage to authenticate myself and submit requests. On top of that, in the token_required() below I coded a section that expects to receive JWT and decode it:
def token_required(f):
#wraps(f)
def decorator(*args, **kwargs):
token = None
if 'jwt-token' in request.headers:
token = request.headers['jwt-token']
if not token:
return jsonify({'message': 'a valid token is missing'})
try:
current_user = False
# for authentication via the swagger-ui
if token == 'my_awesome_password':
current_user = 'admin'
else:
data = jwt.decode(token, app.secret_key)
current_user = 'admin' if data['public_id'] == 'admin' else False
if not current_user:
return jsonify({'message': 'token is invalid'})
except:
return jsonify({'message': 'token is invalid'})
return f(*args, **kwargs)
return decorator
The problem is with the dashboard application:
On the dashboard app, I configured the /login route to generate JWT (which needs to be sent to the api app in every HTTP request I made), then do a set_cookie() with httpOnly=True flag (for security purposes), but then how can the JavaScript access it when it has to make XHR requests? Having the httpOnly flag on makes it unreachable.
If using httpOnly=True is not the ideal way in this case, how can I tackle it and make sure the JWT will always be sent to the api app in the request headers?
I'm using PyJWT package.
Thanks for any help.
If you use JWTs for XHR requests, then you don't necessarily need to use cookies – the browser/consumer could store the token and attach it to every request. If you do want to use cookies (this is also a viable option) then you don't need to worry about attaching the JWT as the browser will do that automatically for the domains you specified in the set_cookie command.
Please help. I am trying to search for a specific user in Foursquare but for some reason I got Missing credentials error 401.
user_id = '484542633' # user ID with most agree counts and complete profile
url = 'https://api.foursquare.com/v2/users/{}?client_id={}&client_secret={}&v={}'.format(user_id, CLIENT_ID, CLIENT_SECRET, VERSION) # define URL
# send GET request
results = requests.get(url).json()
user_data = results['response']['user']
# display features associated with user
user_data.keys()
As documentation states, this endpoint is meant to be accessed on behalf of the user:
This endpoint requires user authentication.
User calls require a valid OAuth access token in the query string of each request instead of the Client ID and Secret (&oauth_token=XXXX).
For more information about this authentication method and how to obtain an access token, see the Authentication docs.
This means v2/users can only be accessed by your app, after a user (which can be you, using your own Foursquare account) goes through the OAuth login flow and grants necessary permissions. OAuth doesn't mean your app "logs in" as the user, rather that the user has given you permission to do something on their behalf.
To learn more about OAuth you can watch this talk: https://www.youtube.com/watch?v=996OiexHze0
To read more about Foursquare's API visit their documentation site: https://developer.foursquare.com/docs/api/
I have been following this Flask pyjwt guide, however my web app is somewhat similar to Miguel's microblog example that uses render_template() and redirect(url_for(...)) for navigation.
I have implemented an encoding and decoding service in my application, however I do not know how to properly return the encoded JWT token to the user using redirect()
My code is as follows:
#app.route('/', methods=['GET', 'POST'])
def login():
login_form = LoginForm()
username = login_form.username.data
password = login_form.password.data
if is_user_valid(username, password):
return redirect(url_for('home'), auth_service.encode_token(username))
render_template('login.html', form=login_form)
My problem is placing the auth token inside the redirect method causes a page to appear saying "Redirecting... you should be redirected, if not click here", which I do not want. I do not particularly wish to change my redirect methods to something similar to make_response(jsonify(...)) as I would then need to handle these responses in the front end when this is a simple login page.
How should I be returning the auth token to the client correctly?
Typically you attach it to response headers which is what your redirect method returns.
response = redirect(url_for('home'))
response.headers['X-JWT-TOKEN'] = auth_service.encode_token(username)
return response
However, I'm not sure if this is gonna be very useful in your setup when using render_template, because you can not easily access these headers on the frontend.
You have two alternatives here:
Make your login endpoint an API. This way you can return the token for the client-side code to handle and store it somehow.
Drop the JWT and stick to using sessions. I know that this is not what you expected, but outside of building APIs JWT-based auth is not very helpful.
I'm trying to make a login with Facebook.
What I want is to create and endpoint and use it as I use the "standard" email login endpoint, since I need a big separation between the backend and the frontend.
I think it should be easy to do it, but I don't know how to do it.
I read this and when I use the Url in the browser, it works properly and I get the token in the response Url. But, it happens always on the frontend side.
I tried many tutorials this one is an example, and it works, but I'm the backend, I'm not allowed to have something like that, as the frontend is written in Django too.
So, I don't know how should be the workflow when you're just the Backend, I don't know what the Frontend developers wait from me because the authentication happens actually on the frontend side.
And I'm a little bit lost.
Maybe someone had the same problem as Backend and could help me, at least tell me, how the workflow backend - frontend should be.
Facebook JavaScript/Android/iOs SDKs lets the client to authenticate the users. Once the user is authenticated with facebook, your clients can send the accessToken through a HTTP POST over https.
This is what I have done in a similar situation,
At backend,
Create API endpoint to authenticate user by validating their accessToken,
POST /auth/
Use this endpoint to verify the accessToken sent by the client. The token should be validated calling Facebook services with your app secret. Once done validating, return a response as a JSON detailing the status of the authentication and user identification details if successful.
on the request,
body should contain accessToken as a key/or a header
Content-Type header should be application/json
any additional expected headers must be validated
on the request try to include
status of the operation
user identification detail if operation is success
a JWT or some sorta token to identify the user which users can include in Authorization header, so that you can validate the request just buy validating the token against User. Set an expiry as the accessToken if JWT is expired, refresh accessToken at client side and validate again.
At Frontend.
Let the client do the following to authenticate themselves.
send accessToken to /auth as a POST request.
if authentication status is success, let them store the JWT in locally and use it on the upcoming requests.
at backend on upcoming calls,
if token is expired or tampered, redirect client to authenticate with Facebook again.
on logging out of user, delete the token from client.
So for the frontend developers,
Document your API properly and share it with them