-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JWT with Firebase for secure authentication #991
Comments
That's the point about JWT: it's not about granting access; it's about authentication. Once uniquely authenticated, the granting process should be implemented at the application level. For example, by creating a local account with the sub-contained data or matching the user with an existing local account. Logging out is also not straightforward. That's why JWT tokens have a very short lifespan, and strategies like key rotation or refresh tokens are used to periodically validate who has access and who does not. A JWT token consists of a header, a payload, and a signature. With each request, you can verify the payload content and match it with your authorized users. If they are not your users, you can register them in your database or remove them if necessary. to check the payload u can enable the authorisation middleware ...
'middlewares' => 'cors,json,jwtAuth,authorization,customization',
... and then ...
'authorization.recordHandler' => function ($operation, $tableName) {
if($tableName === '<YourSensibleTable>')
return 'filter=<userIdField>,eq,'.$_SESSION['claims']['sub'];
return false;
},
... |
Thank you for your response @nik2208, I really appreciate it. I am a mobile developer with no deep experience on the backend part so I apologize in advance for any concept error that I may say. My idea was precisely what you mentioned: to use the 'claims' that come in the 'payload' part of the token to securely authenticate who is accessing my API. However, I still don't understand how the API ensures that this token belongs to my Firebase project and not someone else's. If someone knew that [email protected] was registered in my database, that person could create a project in Firebase, create a fake user with that email, and obtain a valid token. Then, if that person sent a request to my API with that token, my API would let them in and believe it's the real [email protected], because the 'claims' would contain that email. Am I wrong? Thanks a lot for your help |
U usually share/set the secret key with/on the issuer (firebase in the case) relevant setup page, if u can actually verify the signature (keys signed with other audience's keys would eventually fail when attempting to verify them with your secret key, does it sound?) it means that that particular token has been issued using your keys and not others. You would have access to the claims anyway, whether the jwt signature verification process fails or succeeds. |
@nik2208 |
well, I don't know how private is your firebase project Id, the shared secret key should be the way to do it. What's the content of the decoded jwt token (paste it here) u get from firebase? |
But even if my project ID was leaked, no one could ever create a valid token with the 'aud' claim of my ID project, because they can't sign valid Firebase tokens without the Firebase private key. And firebase generates that token based on the authentication. Right?
{ { |
but I can generate a jwt token containing this exact payload (no need to be firebase the issuer). The only tool u can make use of is the signature verification, that states that the token has been issued and sealed using ur key, that supposedly is owned only by you and firebase. |
@nik2208 But that check is already done when using the jwtAuth and authorization middlewares, isn't it? I mean, currently I use this on my config, that (at least it is what I understand) check that the token is signed by Firebase, so even if you send me a token with that payload, the check would not work, isn't it?
If I am missing something I apologize, I try to understand how can I check the signature verification on this API, like you said. Thanks again |
are u manually fetching the jwt token from firebase? |
@nik2208 Yes, my app calls the native Firebase iOS SDK to do the login, and if the login is successful, I call the |
your app should store the received token in the headers, now instead, if I understand the code above, ure actually using the jwt code as secret, when the secret should be the same key you stored in firebase to sign the token |
have a read here #926 |
@nik2208 With the jwtAuth and authorization middlewares, I thought what api.php did was to take the token provided in the header and try to see if it's signed with the key passed in the 'secrets' variable, which is what I currently get by doing what I mentioned before:
Does api.php work different from what I've just said? I am not using the jwt code as the secret. The https://www.googleapis.com/robot/v1/metadata/x509/[email protected] returns the current public keys for Firebase, which are refreshed fairly often, so that's why I added the function to retrieve them. So, I understand that api.php checks if those public keys matches the X-Authorization token provided, and if it matches, it means that it was created using the private key stored in Firebase. Am I wrong on this? If so, what should my jwt.secrets contain? I had previously read the other issue you mention (#926) before creating this issue, but considering that Firebase fully handles my authentication, I believe I don't need anything more than this api.php, right? I understand that my doubt is about how to place my private key in api.php, which was really what motivated me to create this issue. |
maybe I got lost.. are u able to login? does everything work properly? ur doubt is about how to grant access properly?
executed? furthermore: is firebase implementing key rotation or refresh token? |
@nik2208
Yes, everything works properly, I am able to login and everything works as expected, but I was concerned about the possibility that someone could enter my API using another Firebase project to obtain a valid 'Firebase-signed' jwt token. But after researching, I think that if api.php checks if the token provided matches the public keys of Firebase, and if so, then check some data in the claims, such as the project ID (which is unique), I think it would be safe.
I added this code in the api.php, so the secrets (public keys from Firebase) that the config of api.php use are retrieved in runtime, because Firebase refreshes the token regularly. At the end, I do not know the private keys of Firebase so I could not add those anywhere. |
Thank you @nik2208 for explaining the concepts to @kildos. @kildos thank you for your kind words on the project, I'm glad you like it.
Yes, 'api audience' is used to ensure you are being authenticated for usage on the correct project.
See:
And you don't have to implement the audience check, it should be part of the configuration:
See:
It seems your questions are mainly about how OAuth with JWT tokens work and not about the PHP-CRUD-API open source project. Please read the PHP-CRUD-API readme and check out the implementation examples and ask questions relating to the documentation and those examples instead. It would be much more useful to others and serves the same purpose. The firebase example uses a Javascript library (firebasejs v6.0.2) to operate, see: https://github.com/mevdschee/php-crud-api/blob/main/examples/clients/firebase/vanilla.html#L21 Questions about the functioning of firebasejs (f.i. how it sets the audience) should be asked here: https://github.com/firebase/firebase-js-sdk NB: Friendly reminder that this is not a Firebase support forum and on a more personal note: I don't recommend using Firebase as there are many more privacy-friendly alternatives. |
@mevdschee Thank you for your response. My main doubt was in verifying the authenticity of the JWT, but I hadn't seen the possibility of indicating the audiences in the config. I apologize for the conversation veering off the main focus. We can consider this closed, as I now understand how to proceed. Thank you again for your work, and thanks to @nik2208 for his help and patience. Greetings to both of you! |
@kildos I re-read what you wrote and can only agree with @nik2208 that authentication is not authorization and both topics shouldn't be confused and/or merged. If Firebase says somebody has a specific email address and that claims comes verifiable from Firebase, then I would say the authentication process is done (and you should trust that to be true). Realize that whether or not the user with that email address has permissions in your API shouldn't depend on some projectid or audience. Nevertheless I explained how the audience can be checked, but this is mainly to prevent a stolen jwt to be used on other projects (reduce the impact of a leaked token), not for authorization. To summarize: The authentication middleware (Firebase) tells you who the user is and the authorization middleware decides whether or not that user has access to your API (how is up to you). |
@mevdschee Once authenticated, I have implemented ways to determine what the authenticated user can access or not. However, what I was referring to was the concern that api.php (which acts as the gatekeeper for every remote request) could receive fraudulent authentication. After seeing that I can add 'audiences' and 'issuers' filters, my doubt (and my concern) is resolved. I hope I have been understood. |
Yes, those filters can help to reduce the impact of stolen tokens (from other Firebase applications or clients). You seem to have thorough understanding of this difficult subject. I think you have and are understood :-) |
Yes, I think |
I watched this and this and I hope it is not how Firebase works as passwords are not hashed nor are email addresses verified. I already wasn't a fan of Firebase, but honestly this is kind of shocking (if I understand it correctly). I really can't recommend using Firebase as authentication provider now that I have looked (very shallow) into how it works. |
@mevdschee I think it would be nice to include in the API documentation that, in the case of Firebase, it's important (or even mandatory) to check other parameters such as the issuer or the audience. I believe in this case, perhaps I wasn't understood earlier because you assumed Firebase's authentication system was more robust than it actually is, but as long as Firebase does not require the email verification for their users creation and login, someone can create legitimate jwt codes, signed by Google, that authenticates an attacker with any email he wants. In my case, I chose Firebase for the simplicity of the SDK for iOS and the easy-to-implement user flow to recover their passwords, although I may need to reconsider it. Thank you for keeping follow on the issue and your research @mevdschee ❤️ |
Yes, that's true. And it seems that Auth0 has a similar "problem", so it is not even Firebase specific.
I agree, at least the audience requirement should be set for proper security, I'll add that to the readme. How would issuer help?
Thank you for doing the same. |
@kildos Do approve of the additions to the readme? Is it clear enough? |
I've been making improvements to PHP-API-AUTH maybe at this point it could be valuable to invest some time on it. What I want to achieve is a self-hosted identity provider acting as hub for other identity providers (firebase, auth0, facebook, whatever) merging the incoming user and the local user before reaching the actual app. Still missing a key refresh/rotation, reasoning abt whats the best solution. but it works quite well. it needs proper configuration (i mantained the structure @mevdschee gave to the project) but is very flexible and handy once u find yourself around (I can easily define jwt access also on development environment). As soon as I'll find time to abstract my implementation I'll make a PR if u think it can be useful. |
Well, in fact, it's practically the same as the audience, but adding Google's base URL. Since the project ID is unique, if the audience is configured, it's enough.
Yeah, that's great. Very clear, I'm sure it will be helpful for everyone. Thank you for your listen and to @nik2208 for what he's doing. If there's a PR soon, please mention me so I can implement any new behaviour on this ❤️ |
After a few months of investigation and studying, I've eventually accepted that reinventing the wheel could not be the right approach. What is more (much more) interesting, is using supertokens beside this api, and I could make it work. The architecture is explained here |
Hello! First of all, I want to thank you for your great work on this API, @mevdschee. I've used it in a couple of projects, and it has worked seamlessly.
Now, let's get to the point. I want to use Firebase for user authentication, but there's something I don't quite understand how it works. If the 'secrets' part use obtained data from the Google public keys at:
as suggested in this Issue (#708).
How does this API verify that a user has logged into my legitimate app (and not other) if it only checks public keys? In other words, if someone creates a new Firebase project and adds my email, and that person logs in into his project with my email, he would obtain a valid Firebase token, so...could they call my API with that token, and would the middleware allow the access since it's a valid token for Firebase? My question is: where does the API supposedly verify some kind of private key?
I feel like I'm missing something, and I'm a bit confused about this.
Thanks again for your work, @mevdschee ❤️
The text was updated successfully, but these errors were encountered: