My problem currently is that when a user wants to create an account on my app (using Firebase) the following error occurs:
2017-07-12 21:57:05.958 GeneralMessage[8525] [Firebase/Database][I-RDB038012] Listener at /users failed: permission_denied
I realise that the error occurs because the user attempts to read from the database without being signed in to an account (authenticated). The problem also being that the user will NEVER be authenticated at the time they are testing if a name is taken (before they make the account).
I know that you can edit the Realtime Database rules to allow unauthenticated users to access the database but I also read that this was a security hazard and should only be used during testing.
My idea was to temporarily authenticate the user when they click the 'Create Account' button but I am also unsure about how I would do this.
Thank you for any help :)
If you want unauthenticated users to be able to read part of your database, you'll need to modify your security rules to allow that. There is no way to do this temporarily for a specific app instance, since that would require each app instance to be identified - like a user.
I know two approaches:
Sign the user in with anonymous authentication first
Store the user names in an unprotected part of the tree
Sign the user in with anonymous authentication first
Alternatively you can initially sign the user in with anonymous authentication:
Auth.auth().signInAnonymously() { (user, error) in
// ...
}
This only partially secures data access: since the user doesn't prove their identity, it's relatively easy for anyone to still get access to the data. That's why I recommend limiting the data they can access by...
Store the user names in an unprotected part of the tree
This approach is quite common: you want the usernames to be publicly readable, so you store that in a separate part of your database.
usernames
"T. Dess": 8295647
"Frank van Puffelen": 209103
And then with your security rules:
{
"rules": {
"users": {
".read": "auth !== null",
"$uid": {
".write": "auth.uid === $uid"
}
},
"usernames": {
".read": true
}
}
}
So with these rules:
Anyone can read the usernames
Any authenticated user can read the user profiles
Each signed in user can only modify their own user profile
Related
I am trying to create "child" accounts for a registered user.
So, after signing up and authenticating the account I would like to give that user the possibility to register further accounts; for other people to use without the need of an additional email or authentication. These child accounts would be linked back to the main user and that main user can delete/update them.
I am not sure if this is possible with Firebase. I have done some research but have not found a simple or any solution.
Thank you in advance.
The answer is yes, you can do that but there are caveats.
The biggest one is an admin/user situation is that when .createUser is called from the admin account (on the device) it will automatically log IN the createdUser and log OUT the admin. There are a number of posts regarding this behavior.
There are a number of options but one that I would suggest is to leverage the Firebase Admin Auth API which allows you to manage users without having to continually utilize the Firebase Console or do one of the workarounds required on the client side. A node.js example of creating a user looks like this
admin
.auth()
.createUser({
email: 'user#example.com',
emailVerified: false,
phoneNumber: '+11234567890',
password: 'secretPassword',
displayName: 'John Doe',
photoURL: 'http://www.example.com/12345678/photo.png',
disabled: false,
})
.then((userRecord) => {
// See the UserRecord reference doc for the contents of userRecord.
console.log('Successfully created new user:', userRecord.uid);
})
.catch((error) => {
console.log('Error creating new user:', error);
});
what I am trying to do:
I have an app that takes in login credentials: username and password for a user. I have a rest api that internally calls the keycloak REST API: /auth/realms/realmname/protocol/openid-connect/token
and gets the access token for this user.
Now I am building another REST API to access a resource where I want to do the following:
doSomething(accesstoken, data)
{
a) call keycloak API to validate access token and get roles.
b) if role == manager, process(data)
c) else: return error msg.
}
Now, how do I do (a): validating the access token and getting the roles associated with it.
I know we can do: auth/realms/realmname/protocol/openid-connect/userinfo
but that only gives the details about the user like name, email, etc. but does not display any roles.
Here's an example I got:
{
"name": "test user",
"sub": "e2bad34d-a1a9-4d70-ac84-bd3a3246023e",
"email_verified": false,
"preferred_username": "user",
"given_name": "test",
"family_name": "user"
}
As seen, it doesnt give the roles at all. How do I then tell what roles this access token has? Interestingly, when I search for this, many resources are suggesting the above userinfo endpoint. But this merely tells me taht the access token I provided is valid. Does not give roles for that.
In other words - it authenticates but does not authorize.
Please suggest.
Thanks,
Anand
In Keycloak admin Console, you can configure Mappers under your client. Add a builtin Mapper of type "User Realm Role", then open its configuration e.g. change Token Claim Name if you want.
Client roles can be configured similarly, but they are returned by default in the token under the name resource_access.${client_id}.roles
The the client side you can parse the token to find the roles. E.g. In an angular application and using the keycloak-angular adapter, you can have a the token as a json object by calling keycloak.getKeycloakInstance().tokenParsed.
In a spring boot application and using the Keycloak java api, you can find the roles under the field "otherClaim" in the following class
https://www.keycloak.org/docs-api/10.0/javadocs/org/keycloak/representations/AccessTokenResponse.html
In both representations you will find the roles under the "Token Claim Name" defined in the client mapper configuration
Additionally, if the full scope is not allowed then you need to add the relevant roles to the scope, so they can appear in the token.
After adding role in the roles section , need to move available roles into the Assigned Roles of the scope tab of the respective client section.
I am using App ID as an Identity Provider and Authorization Server to protect some back-end spring-boot applications.
I have managed to set up the whole OAuth 2.0 Authorization Code flow to work but cannot manage to include custom scopes into the access token.
The only scopes that appear in the access token are the App ID default ones:
"openid appid_default appid_readuserattr appid_readprofile appid_writeuserattr appid_authenticated"
I have configured an appropriate role with the desired custom scopes and associated this role to the user profile. Furthermore I have associated these custom scopes to the client application. Everything seems fine in the App ID dashboard.
However when I call the token endpoint either programmatically or via curl I always get the same default scopes in the access token.
Reading the Swagger , I should be able to specify the scopes for the password flow and bearer token but I am in an OAuth 2.0 Authorization Code flow. Furthermore, even with password credentials flow, I do not manage to get these custom scopes although I specify them in the request.
Has anyone encountered these problems?
Any help would be much appreciated.
Many Thanks,
Chris
In order to see the application configured scopes in the token, you need to authenticate with the application that you configured scopes to and with the user you assigned the role to.
Meaning you should use username : client ID and password : secret of the application in the request authorization header, and authenticate with the user you assigned the matching role (which contains the scopes wanted).
The steps to add access control to your application:
Go to Applications and define the application that you want to protect by adding scopes.
Create your roles by going to Roles and profiles > Roles > Create role.
Assign the roles to specific users by going to Roles and profiles >
User profiles. Then, choose the user that you want to assign the
role to and click the More options menu > Assign role.
For more information see AppID Access control docs: https://cloud.ibm.com/docs/services/appid?topic=appid-access-control
I have an App ID instance in us-south, and scopes are working fine for me with default Cloud Directory.
create a new application (define your scopes)
create a role and associate your application scope
assign the role to a user
call /token endpoint
It happened to me before, I found that one way to solve it would be to inject the roles into the token claim and then instruct Spring Security to extract them. I wrote about it here in detail. The documentation explains the first part, but the gist is this cURL snippet :
curl -X PUT "https://$REGION.appid.cloud.ibm.com/management/v4/$TENANT_ID/config/tokens" -H 'Content-Type: application/json' -H "Authorization: Bearer $IAM_TOKEN" -d '{
"access": {
"expires_in": 3600
},
"refresh": {
"enabled": true,
"expires_in": 2592001
},
"anonymousAccess": {
"enabled": false
},
"accessTokenClaims": [
{
"source": "roles"
}
],
"idTokenClaims": [
{
"source": "saml",
"sourceClaim": "attributes.uid"
}
]
}'
You can also do it in the Swagger UI. Note however that this is a PUT request, so it's going to overwrite any configuration you had beforehand. Ideally, run a GET request to get the current configuration, then add the claims into it to avoid issues.
Then, in the SecurityConfiguration, add this JWT converter :
protected void configure(HttpSecurity http) throws Exception {
http
//...
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(jwtAuthenticationConverter());
}
Converter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("authorities");
converter.setAuthorityPrefix(""); //so that the role has the same name as the one that comes from App ID
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtAuthenticationConverter;
}
Now that Spring Security recognizes the roles, you can protect endpoints with annotations or with an antMatcher configuration :
.antMatchers("/api/admin").hasRole("ADMIN")
is there a way to manual approve the posts that is being posted to the firebase database through the rules?
the current rule is like this:
{
"rules": {
".read": true,
".write": true
}
}
what I meant is to allow the users to post from into database, but I as an admin control the posts either approve or reject the post from firebase console, is that possible through the rules?
like this Manual approval / rejection of user registration by admin using Firebase
There is nothing built into Firebase for such an approval queue, but you could definitely build it into your app on top of Firebase.
What you essentially do is create a so-called moderation queue, that the users post to. So you end up with two top-level nodes:
posts
post1: ...
post2: ...
post3: ...
pending
post4: ...
post5: ...
The users of your regular app only see the data from /posts.
Then you create a separate app for your moderators, and that app shows the posts in the moderation queue (/pending above) and gives them the option to approve or reject them. If they approve the post, it is added to the actual list of postings that users of the regular app see.
If the app for your moderators is running in a trusted environment, you could consider using the Admin SDK, which ignores the security rules and always has full access to your database. In that case your rules could be as simple as:
{
"rules": {
"posts": {
".read": true
},
"pending": {
"$postid": {
".write": "!data.exists"
}
}
}
}
This allows anyone to read the posts, but only administrators can write them. On the other hand, anyone can write to the moderator queue (as long as they're not overwriting existing data), but only administrators can read from there.
I am creating an online survey tool.
As an administrator, i would like to see what the users have answered and also be able to answer on their behalf. The system get's a users answers and other information based on his/her username, when they are logged in, using the built in membership provider.
There are currently three roles: Administrator, Moderator and Respondent
If i would like to show my administrator a list of users,
how would it be possible to create a "backdoor" for the administrator, so that he can "log" in as the user, see the users answers etc ? (Just like the user would be able to if he was logged in to his own account).
When answering and retrieving quyestions, the system is bound to `User.Identity.Name
My suggestion on how to solve this:
Currently, when i want to retrive a users answers i use the following code:
Firma_ID = db.Firma.Single(x => x.CVR_nummer == User.Identity.Name).firma_id;
var answers = db.Tabelform_Answers.Where(x => x.question_id == model.Question_ID && x.respondent == Firma_ID);
This is because i have a table named Firma, that has a column referencing to a users Name, called CVR_Nummer. I then retrieve all the records in the Tabelform_Answers table, that match question_id and Firma_ID (A users answers for a specific question).
Instead of using `Firma_ID = db.Firma.Single(x => x.CVR_nummer == User.Identity.Name).firma_id;
to retrive the Firma_ID of a given user, i could store it in the Session upon Login. When i want to view a specific users Answers as Administrator, i would then just change Firma_ID in the Session. Changing Firma_ID in the Session would only be allowed through a controller which has the following code:
[Authorize(Roles = "Administrator")]
Also, i would set the Session timeout to be the same as the Authentication timeout.
Can somebody tell me which pros and cons of this solution? Are there any other ways of storing a "global" variable for a Session? (Firma_ID)?
Thanks
If you only need to log in as your users, I went for a ticket-method.
I have a special login-page that can take a ticket-id. This ticket is created in the admin-gui when the admin wants to log in as another user. The login-page checks the ticket in the database, logs in the wanted user, and then deletes/marks the ticket as used. As an added security, a ticket is only valid for 10 seconds after creation.
Another option is to make answers from users available from the admin-gui...
also you can do in your log-in script override
so you have at present something like
if user name and password match string then user is logged in and based on this you get user permissions
instead have admin page,
where you can select user and then you can apply permissions of the user instead of admin.