I'm creating a web application using Firebase as the backend for data storage, real-time updates, as well as for hosting.
Moving most things client-side is pretty slick, but the issue of security is an important one to address.
For access to data on Firebase, Firebase Security Rules takes care of most things. But, when using Firebase for hosting, I feel like there is a lack of security for accessing certain routes.
Currently, on a page load, I can check to see whether or not a user is logged in. If the user isn't logged in (or if the auth token isn't valid) I can redirect the user to a different page (i.e. a Login page). But, my issue is what if there is information embedded in the static html on that page that I wouldn't want an unauthorized user to see?
I feel like the first answer that I'll receive is, "The data should be kept in a Firebase variable and loaded only if the authorization is successful." While that is a valid option, I'm thinking storage of HTML (or even just paragraphs of text) as a Firebase variable is kludgy, and there ought to be a better way.
Initially I thought that this would be an inherent option in the firebase.json file, as one can define redirects, headers, etc. But, there is nothing in firebase.json for security (like in firebase-security.json) that would allow me perform an auth check such as the following:
{
"firebase": "myfirebase",
"public": "app",
"ignore": [],
"rules": "config/security-rules.json",
"routes": [ {
"source" : "/for_authorized_only/",
"destination": "/authorized_page.html",
"auth": true, //Must be authorized
}, {
"source": "/some_public_route",
"destination": "index.html",
"auth": false, //No auth required to access this page
} ]
}
I haven't tried out AngularJS or AngularFire, but after searching a little it seems like the angularFire-seed project includes route security, however this should be included in Firebase rather than rely on another framework!
Is there something that I'm looking over that would allow me to accomplish this?
Related
I am using Hasura with Firebase and Flutter. When a password-less login/signup link is sent to the user for the first time, I need to intercept the account creation process to add some custom claims before they are automatically logged in. If they are automatically logged in then the custom claims won't be present and permissions will be incorrect.
Is it possible to have a custom Firebase function that I could call to create a "password less" account with the custom claims before I process the magic link? The only call I can see is createUserWithEmailAndPassword which is not the right method...
Another option (less attractive) is to process the link using signInWithEmailLink(), apply the claims to the account using a firebase function, then force a new token (which will have the new claims) via _firebaseAuth.currentUser.getIdTokenResult(true).... would an onAuthStateChanged be triggered on a forced token refresh?
Is it possible to have a custom Firebase function that I could call to create a "password less" account with the custom claims before I process the magic link?
No, it's not possible. Functions can only respond to the creation of a new account after it happened. They can't intercept that process to change the custom claims for a client app that just signed in immediately after an account was created.
would an onAuthStateChanged be triggered on a forced token refresh?
No, it wouldn't. If you force a token refresh from the client app, you will only receive an update from the idTokenChanges stream.
Changing the custom claims on the backend using the Firebase Admin SDK will not force a propagation to the client app. If you want to wire this up yourself, you can do so following something like the process in this blog post. The backend will somehow have to push some data to the client app to get it to force refresh the user's token to take effect immediately.
For anyone that lands here, in the end I used Hasura Claims Map https://cantaspinar.com/easier-authentication-with-hasura-jwt-claims-customization-firebase-auth/ to apply default claims. This means all people who log in get the "user" claims by default, unless there are any claims applied on firebase which will then be used instead.
My claims map looks like:
"claims_map": {
"x-hasura-user-id": {
"path": "$.user_id"
},
"x-hasura-default-role": {
"path": "$.['https://hasura.io/jwt/claims'].x-hasura-default-role",
"default": "user"
},
"x-hasura-allowed-roles": {
"path": "$.['https://hasura.io/jwt/claims'].x-hasura-allowed-roles",
"default": [
"user"
]
}
}
}
According to the firebase official documentation, to set up a custom route like /link in your custom domain like example.com, I need to use firebase hosting already and do the following configuration there:
"hosting": {
// ...
"appAssociation": "AUTO",
"rewrites": [
{
"source": "/link/**",
"dynamicLinks": true
},
]
}
But I'm not using firebase hosting, and Obviously, I don't have such a configuration.
So I can have the same result with no firebase hosting?
This has worked for me in the past with custom domain names for dynamic links: when you get the error in the dynamic links page, open the Authentication(you don't actually need to use firebase authentication on this project for this to work), go to sign in method, enable Email/Password then scroll down to Authorized Domains and add your domain as an authorized domain. This won't affect your current website or email. Go back to hosting and try to set up the URL prefix again.
I am using auth0 for authentication. I want to fetch all users including their roles. I generated token in auth0 and when I try to execute it in Postman or fiddler tool, Sometimes it's giving roles and sometimes not. Same thing is happening in application also.
If I add manually in app metadata in role property as below, Then information is coming.
{
"authorization": {
"groups": [
"Admins",
"Users"
],
"roles": [
"Admin"
],
"permissions": []
}
}
But I fill, that if I change in authorization tab, It should effect here also.
Below is my code,
var apiUser = new ManagementApiClient("<<Token>>", new Uri("https://<<Domain>>/api/v2"));
IPagedList<User> allUsers = await apiUser.Users.GetAllAsync();
Do I need to clear cache in auth0, If yes then how?
Based on the information you provided it seems that you're using the Auth0 Authorization extension to configure user role information.
If this is the case you should notice that the extension logic is run at login time by the means of a rule. When you have that extension installed you should also have a companion rule; in my account the rule is named auth0-authz and should be the same for your case assuming version 2.0 of the extension.
The impact of this is that the roles are surfaced at the user level at login time, so any changes to the configured roles will be seen next time the user logins.
Note: Since this logic is part of a rule it will only be executed in the context of a login. If users are added to or removed from a group this will only be reflected within Auth0 after this user logs in again (eg: in the user's app_metadata or when calling the /userinfo endpoint).
You're querying the users directly through Auth0 Management API which may lead to the situation where the roles currently stored at the user profile are not up-to-date. If you are seeing stale information then this might be the cause.
On the other hand if your problem is not exactly this one, please provide further information and if possible steps to reproduce. For example, do the roles information show for one user but not the other or does it show for user A in one response, but then if you make another request the response does not include role information for that same user A?
I was not using the authorisation extension, but rather the standard role. So I've had to create the below rule.
More info here : http://isbyr.com/return-user-roles-in-auth0/
function (user, context, callback) {
// Get the user roles from the Authorization context
const assignedRoles = (context.authorization || {}).roles;
// Update the user object.
user.rolez = assignedRoles;
callback(null, user, context);```
I am developing an app with firebase and angular and angularfire. I would like to implement a
"invite collaborators" feature in much the same way as firebase itself implements collaboration - that is, the app user can enter an email address to invite collaborators which would send an email and generate an "inviteToken", just as is done when inviting collaborators in firebase itself.
I understand that security rules (to limit collaborator access) and schema design( a /collaborators 'folder' ? ) are one aspect, which can be accomplished using native firebase and angular. My question is how to best implement the invite email and the 'inviteToken'? What would be the most expedient way to implement such an invitation feature? Could it be done using native firebase? Or would one need to implement separate, server side code (nodejs?)? Perhaps someone from the firebase team can opine based on how firebase itself implements collaboration.
You can implementation collaboration by hashing the email address of the user you want to share a particular piece of data with, and storing it under a permissions field.
For example, let's start with a path /items/item1 that is owned by 'user1':
{
"items": {
"item1": {
"data": "foobar",
"permissions": {
"user1": true
}
}
}
}
You'd set the security rules for the data as follows:
{
"rules": {
"items": {
"$item": {
".read": "data.child('permissions').hasChild(auth.uid)",
".write": "data.child('permissions').hasChild(auth.uid)"
}
}
}
}
Now when 'user1' wants to share 'item1' with 'user2', they will simply write the value 'user2' and set it to true under the permissions key. You can extend the structure of the 'permissions' key to be as granular as you want (eg: collaborators can only read, but owner can both read and write, etc.)
In practice, you may want to use hashes of the user's email addresses, for example. Also take a look at Simple Login for an easy way to authenticate your users (once authenticated, the auth variable used in the security rules above are automatically set for you).
I'm new to Firebase and I'm attempting to set-up a simple authentication system using e-mail/password. The initial concept is simple: you register. Then, after logging in, you can access the rest of the mobile app.
In the past, I could set this up with PHP in just a few minutes. But with Firebase, this has become a battle that I can't seem to win.
Using the light documentation found on Firebase's site, I was finally able to successfully register and authenticate a user. Great.
Unfortunately, people can still access the rest of the app whether they are logged in or not. How do I keep the app protected from non-authenticated users?
Also, how do I associated data submitted on a page with an authenticated user?
I've looked at Firebase's documentation. It lacks practical examples for authentication. It keeps referring me to the Firefeed app as a sample. I've looked at Firefeed's code and the authentication system seems 1) excessively complicated for a login system and 2) too intricately tied in to news feeds to be a practical example to learn from.
On the other hand, perhaps I'm just missing something obvious and fundamental. If someone could point me in the right direction, that would be great. Thanks! :-)
(By the way, I tried e-mailing this question to firebase-talk#googlegroups.com, as suggested on Firebase's site... but the group does not appear to exist, according to the bounce-back message from Google.)
Stepping back for a moment, it's worth noting that Firebase Simple Login is an abstraction built on top of Firebase Custom Login for convenience. You can still use your existing authentication with Firebase using Custom Login, if you like.
Firebase Simple Login eliminates the need for you to run a server just for authentication. However, there is no 1-to-1 parallel to the PHP example where the server would govern request access based upon a detected session on the server because all of your logic, templates, etc. lives in client-side code.
In most cases, your client-side logic, templates, assets, etc. will be static and public. What you're really looking to secure is user and application data, and this is where Firebase Authentication (whether using Simple Login or Custom Login) comes in. Firebase Authentication is essentially token generation - taking confirmed, identifiable user data and passing it securely to Firebase so that it cannot be spoofed.
Read / write access to different paths in your Firebase data tree is governed by Firebase Security Rules, which allow you to write JavaScript-like expressions to control which clients can access which data.
Here's an example:
Suppose you have a user list, where each user is keyed by user id,
such as /users/<user-id>/<data>, and you want to ensure that only
the logged in user can read / write their own data. With Simple Login,
this is really easy!
Looking at the After
Authenticating
section of Email / Password authentication docs, we see that the
auth variable in our security rules will contain a number of fields
after authenticating, including id, the user's unique user id. Now
we can write our security rules:
{
"rules": {
".read": false,
".write": false,
"users": {
"$userid": {
".read": "auth != null && auth.uid == $userid",
".write": "auth != null && auth.uid == $userid"
}
}
}
}
What's going on here? Firebase Authentication (using Simple Login)
securely generated a token containing your verified user data upon
login, and that token data becomes available in your security rules
via the auth variable for the connection. Now, in order for a client
connection to read or write to /users/xyz, the user must be
authenticated and authenticated as user xyz.
Most of the above is covered in the Security Quickstart but it is admittedly a little hard to wrap your head around.
Back to your initial question, if you want to redirect away from certain paths when a user is not authenticated, you can do the following:
var ref = new Firebase(...);
var auth = new FirebaseSimpleLogin(ref, function(error, user) {
if (!user) {
// we're logged out, so redirect to somewhere else
} else {
// we're logged in! proceed as normal
}
});
Hope that helps!
Please note:
Login is now a core feature of Firebase. Simple Login has been
deprecated and documentation for this client is now available on
Github.
See this page for more info:
https://www.firebase.com/docs/web/guide/user-auth.html