I'm not sure I correctly understood how authentication works in Pusher. Here's a scenario I'm concerned about:
User wants to subscribe to a private channel so the Pusher library calls my server in order to obtain an authentication token.
Server checks if the user is logged in and returns the token
Now the user gets this token and logs out from my app.
User is able to subscribe to the same private channel using the auth token from a different machine, even though they have been logged out.
Is the point 4 valid? Will it be possible to use the auth token after the user has been logged out from my app?
No, option 4 isn't valid. An authentication token is created using a combination of the client's socket ID, channel name and secret. See: http://pusher.com/docs/auth_signatures
The socket ID is a globally unique identifier for the current client connection. If the same auth token were to be used on another machine the socket ID would be different so that auth token would not match the one that Pusher creates when checking the token sent as part of the subscription request from client.
Related
Assuming you have an existing user management/database on a web platform. Sign in with Apple should be integrated for a quicker login and registration process – although it will always create a regular account linked to an email address (just without a regular password). Is it safe to use the (validated) JWT provided by Apple for authenticating?
Signing in (existing account) would be the following steps:
User taps on "Sign in with Apple" in an app
the generated JWT from Apple is sent to the authentication server
the server validates the JWT using the public keys provided by Apple's API endpoint
the server extracts the email from the (validated) JWT and if a user with that email exists, this user is signed in (API returns internal access/refresh token for the session)
I try to craft an answer for iOS apps. But first clarify the question:
"Is it safe to use the (validated) JWT provided by Apple for authenticating?"
The only known JWT we receive from an Authorization task is the "id_token". Other parameters may be JWTs as well, but these are opaque for the client.
The question is now, if we send the id_token to the app server, is it sufficient to just validate the id_token to hand out the client an access token for the app server's domain? Answer: NO!
When using Apple's Authentication Framework for iOS for Sign in With Apple, the Authorization task returns an ASAuthorization value in the completion handler. This contains basically the following parameters:
user: an identifier
identityToken: JWT the "id_token" (see OIDC)
authorizationCode: A short-lived, one-time valid token that provides proof of authorization to the server component of the app. The authorization code is bound to the specific transaction using the state attribute passed in the authorization request. The server component of the app can validate the code using Apple’s identity service endpoint provided for this purpose. *)
*) If that value does correspond to the OIDC "code" value which will be obtained by a client via the "front channel" aka user agent aka browser, then we should also ensure that an additional mechanism is in place which actually provides a secure "proof of authorization" (Universal Links, PKCE), see Authorization Code Interception Attack.
If these attacks are technically impossible, because the authentication system provides secure communication channels with the app, we don't need PKCE, though.
The id_token contains information about the user that has been authenticated which is stored on the Provider. It's a signed JWT. Even if the JWT can be successfully validated, with the JWT alone the app server cannot be sure that the sender is the one who it believes it is. We don't want to give anyone an access token who is not authenticated!
The app server needs more prove and this will be accomplished with the authorizationCode parameter. This check has to be done on the Provider though.
So, we have to perform two steps:
Verify the Identity Token (id_token)
This will be performed on the app server.
Validate Authorization Code
The second step will be accomplished by your app server obtaining a refresh token form the Providers special endpoints.
With Step 2 we receive a TokenResponse.
If this was successful, we receive an access token and a refresh token. The access token is of no use, but we need the refresh token:
"You may verify the refresh token up to once a day to confirm that the user’s Apple ID on that device is still in good standing with Apple’s servers."
Store this on your app server.
Once after this is all done on your app server you proceed with:
Manage the User Session
After verifying the identity token, your app is responsible for managing the user session. You may tie the session’s lifetime to successful getCredentialState(forUserID:completion:) calls on Apple devices. This is a local, inexpensive, nonnetwork call and is enabled by the Apple ID system that keeps the Apple ID state on a device in sync with Apple servers.
A "User Session" will likely require a domain specific access token and refresh token. You will likely verify again Apple's refresh token when the client requires a new access token on your token endpoint.
So, the last step is your app sending the domain specific access token and refresh token to your client.
I am using firebase for authentication with my custom server. This server exposes an endpoint for client to subscribe to server-sent-events. I initially open connection via new EventSource(myApi.com?:firebaseToken) and validate this token via admin sdk on the server.
But I have few questions / concerns:
Is it secure to send firebase token as a url parameter like that?
What happens if user is connected to this event endpoint longer than lifetime of the token, i.e. token now becomes outdate?
If the connection is encrypted, and the client trusts the server, then you can send whatever you want without problems.
The permissions granted by the token will expire in one hour. The client will need to provide a new token before then.
I have read many articles and viewed many videos but there are a lot of contradictions. I try to avoid any external libraries and build the system from scratch, I have read about oAuth 2 but it is more confusing.
This is the flow that I think is ok untill now:
User fills a form using email and password and submits it.
Server verifies the password if it matches and responds back with a httponly cookie with a signed jwt token that expires in like 10
minutes. (I know I have to protect it against csrf attacks)
User gets logged in and every new request he is making to the server he will send the cookie in the header automatically and the
server will verify the token.
Everything is fine but I have encountered some issues and have some questions:
I want the user to stay logged in even after opening a new session so there is no need to login after the token expired or when he closes the browser.
What should happen if the access token expired?
There should be a refresh token attached to the user in database that gets added when the user logs in with an expiration of ex 7 days, then the server will respond with a cookie containing that refresh token?
On the new request while access token is expired,the user will send the refresh cookie to the server, if it matches the user database refresh token,server will respond with a separate cookie that will renew the access token?
If there is a refresh token where should you store it and what format? (cookie,database or where?)
Should I keep the user logged in based on this refresh token cookie?If is it httponly I can't read it and set the state that user is logged in. How should I do it?
I heard about that revoking the jwt token is problematic. How would you fix it?
How would you do this whole thing?Please explain the workflow, I try to avoid localstorage,as I read everywhere that is not safe for sensitive data.
I have implemented and deployed to production systems that do exactly the kinds of things that you are asking about here so I think that I am qualified to provide you with some guidance to solve your particular issues and answer your questions. The flow that you have listed above in the numbered list is definitely the correct path so far. I do understand your confusion going forward from there because there are many different options for how to approach this problem.
In addition to providing a login route that returns a new JWT to the client when the user submits a login form to the server, I would recommend also implementing a token refresh route that accepts a still valid JWT that was received from the initial login process and returns a new JWT with an updated expiration time. The logic for this new token refresh route should first verify that the provided JWT is still valid by matching it with a user in the database. Then, it should generate a new token using the same JWT generation logic as the login route logic. Then, the application should overwrite the access token data in the database for the user replacing the old access token with the newly generated access token. It is not necessary to keep an old access token in the database once it is no longer valid, which is why I suggest simply overwriting it with a new one. Once all of that is finished and successful, you can return the new JWT to the client and then the client should now use that new JWT when making any additional authenticated calls to the server to maintain an authenticated interaction with the server. This logic flow would keep the user logged in, because the client would have a valid JWT before calling the refresh logic and it would have a valid JWT after calling the refresh logic. The user should only be recognized as not logged in and not authenticated if they are no longer able to provide a valid access token that is associated with a user in the database.
As far as cookies go, whichever method that you use for maintaining the cookies on your client should be used for setting the refreshed access token as it is for setting the initial access token that you receive on login. If the server finds that an access token is no longer valid at some point in the future, if for example your client is not used after login until some time after the access token has expired, then the client should recognize a server response indicating that this is the case and present the user with the login flow on the client again so that a new access token can be acquired and stored in a cookie on the client.
I would not worry about revoking JWTs and instead just let them expire if they do and initiate a new login flow if it is found that a JWT has expired. Also, instead of using local storage I would suggest using session storage to store your JWT so that you have it for the duration of your user's session on the website and it is removed as soon as the browser has been closed. This will prevent the JWT from persisting beyond the session and should assuage your fears about saving sensitive data in the session storage. Also, when generating your JWT, you should also make a point of not storing any sensitive data in it because JWTs are easily reverse-engineered. This can also prevent any sort of sensitive data from being exposed on the client.
EDIT:
The key thing to remember when developing your server API is that you should have two different classes of endpoints. One set should be unauthenticated and one set should be authenticated.
The authenticated set of endpoints would not require an access token to be included in the request. An example of this class of endpoint would be your login endpoint, which does not require an access token because it actually generates an access token for you to use later on. Any other endpoint that does not expose sensitive or important information could be included in this class of endpoints.
The unauthenticated set of endpoints would require an access token to be included in the request, and if no access token or an invalid access token is detected the endpoint would respond with a 401 HTTP response code (indicating an unauthorized request). An example of this class of endpoint would be an endpoint that allows a user to update their personal information. Obviously, a user cannot update their own information if they cannot provide credentials to prove that they are the user whose information they are attempting to update. If the client receives a response with a 401 response code, that would be the signal that the client would need in order to tell the user to re-login so that a new valid access token can be retrieved. This possibility can be avoided on the client if the client is programmed to periodically check the expiration of the JWT that is currently being held on the client and initiate an access token refresh, but obviously you should still have logic in place to detect and respond to a 401 response code so that the client user flow is managed properly.
When the user ID token is revoked the information should be passed to client for actions like reauthentication or user signOut().
On the client we can call FirebaseAuth.DefaultInstance.SignOut. How can we achieve the same effect with admin SDK?
Is there a way to sign out the user via admin SDK? (make the auth === null).
A 'hacker' could alter the client code and prevent the app from sign out or reauthentication. I know I can check if the token was revoked with admin SDK and firebase rules but it would be much easier to check only if the auth is null. Or is my thinking wrong as the 'hacker' got the ID token once and can send requests with these credentials anyway even after the client app called FirebaseAuth.DefaultInstance.SignOut?
Is there a way to sign out the user via admin SDK?
The Admin SDK provides a way to revoke a user's refresh token. This does not immediately sign the user out, since the user has an non-revokable ID token which is valid for an hour.
If you want to immediately make backend-resources unavailable to the client whose token you revoked, you can write their state to the database when you revoke the token, and then access that from your security rules. There is no simpler way to block them instantly. Also see Five tips to secure your app, which covers this and security topics.
A 'hacker' could alter the client code and prevent the app from sign out or reauthentication
The client's authentication state is based on an ID token that is valid for an hour after it's been minted. The client cannot extend the token beyond that. To get a new ID token it will have to call to the authentication server, which will fail after its refresh token has been revoked.
Signing the user out on a client makes that client forget the tokens for that user. The ID token would still be valid, but nobody will know about it. Keep in mind: all traffic between the app and the servers is sent over (end-to-end encrypted) HTTPS connections. This means a man-in-the-middle attack is only possible if somebody is able to decrypt this traffic, which requires that they have access to the certificate. See for a better explanation Does https prevent man in the middle attacks by proxy server?.
I know there are already many posts about Oauth, Oauth2, JWT, etc.. I have read many and I more confused than ever so I am looking for some clarification. I will propose my view on the subject and I hope somebody can tell me if my implementation is secure enough or what I am doing wrong and how to improve it.
I am building an API Rest server for serving my resources to my users. Let's suppose it is a bank app where users can deposit, withdraw and transfer money.
I am using nodejs, hapijs, jsonwebtokens, and bcrypt for my server. I want to implement two token authentication flow (Oauth2).
This is the way I am doing it:
User logs in to the auth server by giving some credentials (username and password).
The server verifies the user's credentials, if they are valid, it will grant access to the user and return a refresh token and an access token.
These tokens are saved into the local storage of the browser or mobile device.
The access token:
is signed as a jsonwebtoken.
contains issued date, expiration date (5 min), user data (id, username).
The refresh token:
is signed as a jsonwebtoken and encrypted with bcrypt.
contains a unique identifier
may contain an expiration date
is saved in the database.
As long as the access token is valid, that means, it has not expired and contains valid user data, the resource server serves the user the requested resources.
When the access token is no longer valid, the auth server requests the client to provide a refresh token in order to issue a new access token
The server receives the refresh token from the user, decrypts it, compares it to the one in the database, checks if it has been revoked, and checks its unique identifier.
If the refresh token passes all tests, the server issues a new access token to the client.
If the refresh token fails one test, the server requests the user to re-authenticate.
Notes: I am trying to avoid the usage of cookies.
Questions:
If the user is able to steal an access token, I guess it can also steal the refresh token. So, how can I make the refresh token more secure?
Is my perspective of the Oauth2 flow correct?
What can I improve?
Am I missing something?
The reason OAuth2 is so confusion to many people is because it uses different authentication flows depending on what kind of client is used.
OAuth2 distinguishes two client type, confidential or public. Next to that, there are 2 grant flows that are redirection based (auth code and implicit) which are meant to be used with a browser or browser control.
The other two flows (resource owner password and client credentials) are meant to be used from non-browser apps (CLI, background services, trusted mobile clients).
I've described the different flows and when to use them in more detail in this answer here.