As the title says, I'm developing a Custom User Storage Provider (here forth SPI) with Keycloak 15.0.2.
I’m having trouble sorting an issue where the very first access token that is issued, does not match the expected format (is missing some fields) but also seems to be issued for a different user, if I am to judge only based on the sub field of the AccessToken generated.
To ease reproduction of the issue, you can find my repository on Github here with a complete sample FE and BE along with the keycloak configuration. I also included samples of the result tokens, jwt.io links and logs on LOGS.md file on the repo.
I think I understand why this mismatch is happening, though.
Due to the fact that I start with an empty collection of users on keycloak, I need to create the users on their first login. All I have to start with is their email address which is input on the login screen.
With this information, I setup a “temporary” Federated User until I get the user data from the “real” IDP on the isValid method (where the user actually logs-in into the third party IDP) and then get his details, which are then used to fill a more complete FederatedUser profile and store it on the userLocalStorage.
It's basically this logic (it's all also explained in comments in the repo's code):
Create an adapter/model based solely on the email from the login form to be used temporarily.
Proceed with normal operation.
Then on the isValid() method:
login the user through the REST call to the backend and get the JSESSION token
on a separate call, call the Current-User REST endpoint to get user details and map them to a Dto object
create a new adapter, based on the Dto object (which already contains all the user details like name, phoneNumber, etc) and from that, add to storage as a ksession.userLocalStorage().addUser() user and enrich with custom attributes (to later be mapped into the AccessToken)
when (and if) added, clean cache with ksession.userCache().clear()
Proceed with normal operation
However, I think that the ID/model of that first temporary user is the one that is actually being used during the issuance of the first AccessToken that is generated and is being cached somehow on some other class which then generates the AccessToken with missing information/not the correct user model.
When I reload the page (forcing it to go through the login flow again), I then get the correct AccessToken with all the fields I expected the first one to have. I also noticed that the sub of the tokens are different, and this is what leads me to this conclusion.
Does this flow/conclusion seem correct to you?
And more importantly, how can I fix this?
I have no way of getting all the user data at first or a way to import it (ideally, I didn’t even wanted to Federate, just some ReadOnly data would have been enough if I could modify the AbstractUserAdapter attributes).
Can I somehow access the CredentialInput outside the isValid method?
That’s the only way I’d have to grab all the user data since the beginning.
I’d really appreciate any help you could spare. The reproduction code is just a clone/docker up away and will replicate the issue perfectly.
Please help me figure out how to make sure the token get properly set/issued the first time around
Thanks
Related
So I have a platform that works like this: Users can create accounts by logging in with their Google (I USE AUTH0) and then they can create "Projects" which contain lots of other unimportant stuff regarding my current problem (like todo lists, ability to upload files etc; they can also Edit the project by changing some of it's attributes like name, description, theme and so on). There is a home page where everyone can see each other's projects and access them (but not upload files, change the tasks in the to do lists; this is possible only by the person that owns it).
By using a tool like Burp, people can see the request made from frontend to backend, for example when accessing one of the projects, and modify it on the fly.
This is what it looks like inside Burp when they access one of the projects:
As you can see there is a Get request to /projects/idOfTheProject; they can replace the GET with DELETE for example and they will successfully delete it; they can also see what is sent to the backend when a project is edited (name changed, description, thumbnail picture etc) and change anything they want about it.
How should I prevent this?
What I've looked at so far:
a. JWT - Probably the best fitting for my situation, but required the most work to be done (as I already have my platform almost finished with no such a security measure implemented yet, so I may need to rewrite a lot of things in both backend and frontend)
b. Sending the user's id that initiated the action as well to the backend and verify if it has the necessary privileges - the worst solution as users can access each other's profile and see the id, then just change another field in the request's JSON
c. Have a sort of token for each user and send that instead of the user's id - in this way somebody can't get your token by just looking at the communication between frontend and backend (only if it is using YOUR account). That token should be taken maybe somewhere from the auth0 when they create their account? If they provide something like that; or I can just create it myself and store it alongside the other user variables. You would still see the requests in plain text but even if you modified something you would still have to "guess" the owner's token, which will be impossible.
For frontend I use NextJS and for backend Flask.
Thank you in advance!
The TL;DR is that you don’t. A determined user will always be able to see what requests are being sent out by the code running on their computer and over their network. What you are describing when asking how to prevent people from “sniffing” these requests is security through obscurity, which isn’t actually secure at all.
What you should do instead is have an authorization system on your backend which will check if the current user can perform a given action on a given resource. For example, verifying that a user is an administrator before allowing them to delete a blog post, or making sure that the current user is on the same account as another user before allowing the current user to see details about the other user.
We’re trying to implement a Wopi Host following the protocol to integrate with OWA, as documented in here, and we’re having some issues with some points:
We have implemented a simple host that is only capable of viewing files, that is, it implements the CheckFileInfo and GetFile views. In a test environment, the flow is working and we’re able to view the files in OWA. The point is, when executing the Wopi Validator (the web and the docker version), we’re having an error in the GetFile operation because the validator is trying to access the endpoint with two // at the end:
host/wopi/files/file_id//contents
Is this a known issue that is happening only in the validator? Why are the two ‘/’ being appended to the end of the WopiSrc? How can we address this issue?
We have read some posts here stating that the editing is required in order to officially validate our OWA integration with Microsoft. Is this true? Isn’t the CheckFileInfo and GetFile views the only ones necessary to implement a simple Wopi host capable only of viewing files? We’re just passing the required information in the response of the CheckFileInfo operation. We’re not using FileUrl or any other parameter but the required ones. As far as I can see, these two views are the only one required for viewing files with OWA, such as stated here
Additionally, we’re having an issue in the first part of the flow, when the browser sends a request to OWA and passes the token and the WopiSrc. We were only able to make the flow work passing the token in the query string via the GET method. If we put it under a JSON with a POST method, the OWA simply ignores it and does not make an attempt to call the Wopi Host at all, via the WopiSrc. Could someone enlighten us a bit on this matter to figure out what may be happening?
Furthermore, we’re stuck in some point of token validation. The docs are crystal clear when they say that the token is generated by the host, and that it should be unique for a single user/file combination. We have done that. The problem is, how are we supposed to know what is the user that is trying to access a resource, when the request comes from OWA? For example, when the OWA calls the host in the CheckFileInfo and GetFile views, it passes us the token. But how could we know the user information as well? Since the token is for a single file (which we have in the address of the endpoint being accessed) and for a single user, how can we validate the user at this point? We have not found any header or placeholder value that could be used to extract this information when receiving a request from OWA, and we’re a bit lost here. We’ve thought about appending the user information to the token, and then extracting it back, but for what I could see, doing that I’m only ensuring that the token has not been modified between requests. Does anyone have any idea?
Regarding the validation with Microsfot demands the edit functionality.
For the POST situation, the submission must be made as a "form" not as JSON.
The token validation is completely open, you must choose the way you think would be the best approach. JWT is a good alternative in this case.
I use an authentication service Auth0 to allow users to log into my application. The application is a Q&A platform much like stackoverflow. I store a user profile on my server with information such as: 'about me', votes, preferences, etc.
When new user signs in i need to do 1 of 2 things:
For an existing user - retrieve the user profile from my api server
For a new user - create a new profile on the database
After the user signs in, Auth0(the authentication service) will send me some details(unique id, name and email) about the user but it does not indicate whether this is a new user(a sign up) or a existing user(a sign in).
This is not a complex problem but it would be good to understand best practice. I can think of 2 less than ideal ways to deal with this:
**Solution 1 - GET request **
Send a get request to api server passing the unique id
If a record is found return it
Else create new profile on db and return the new profile
This seems incorrect because the GET request should not be writing to the server.
**Solution 2 - One GET and a conditional POST request **
Send a get request to api server passing the unique id
The server checks the db and returns the profile or an error message
If the api server returns an error message send a post request to create a new profile
Else redirect to the home page
This seems inefficient because we need 2 requests to achieve a simple result.
Can anyone shed some light on what's best practice?
There's an extra option. You can use a rule in Auth0 to send a POST to the /users/create endpoint in your API server when it's the first time the user is logging in, assuming both the user database in Auth0 and in your app are up-to-date.
It would look something like this:
[...]
var loginCount = context.stats.loginsCount;
if (loginCount == 1) {
// send POST to your API and create the user
// most likely you'll want to await for response before moving on with the login flow
}
[...]
If, on the other hand, you're referring to proper API design and how to implement a find-or-create endpoint that's RESTful, maybe this answer is useful.
There seems to be a bit of disagreement on the best approach and some interesting subtleties as discussed in this post: REST Lazy Reference Create GET or POST?
Please read the entire post but I lean towards #Cormac Mulhall and #Blake Mitchell answers:
The client wants the current state of the resource from the server. It is not aware this might mean creating a resource and it does not care one jolt that this is the first time anyone has attempted to get this resource before, nor that the server has to create the resource on its end.
The following quote from The RESTful cookbook provided by #Blake Mitchell makes a subtle distinction which also supports Mulhall's view:
What are idempotent and/or safe methods?
Safe methods are HTTP methods that do not modify resources. For instance, using GET or HEAD on a resource URL, should NEVER change the resource. However, this is not completely true. It means: it won't change the resource representation. It is still possible, that safe methods do change things on a server or resource, but this should not reflect in a different representation.
Finally this key distinction is made in Section 9.1.1 of the HTTP specification:
Naturally, it is not possible to ensure that the server does not
generate side-effects as a result of performing a GET request; in
fact, some dynamic resources consider that a feature. The important
distinction here is that the user did not request the side-effects,
so therefore cannot be held accountable for them.
Going back to the initial question, the above seems to support Solution 1 which is to create the profile on the server if it does not already exist.
I'm in the process of designing my data model for an iOS application where I'm using Parse for the backend (first time using Parse)
Every user of my application has both private data that should be readable and writable only by the owner of the data and public data which should be readable by everyone.
I'm considering keeping my publicly readable data stored in Parse's built in User object and the private data in another custom object. Is there any downside to this? My app uses Facebook Login exclusively and, thus, when a user is created the authData field is populated with "Facebook: 12345..." Is having this accessible to everyone a security problem?
In this scenario the User object (my public data) would need "Get" not "find" permissions, I just need it to be readable by anyone who knows the objectId. So that rules out anyone just dumping my entire User's table.
I've read through the documentation and the "sensitivity" of each user's authData wasn't clear. I realize that if I were using a traditional username/password scheme it would be a problem, but is it for Facebook login? Any help/tips are appreciated.
EDIT:
I printed an entire user object to the console on a test client (with another, different user logged in) and authData wasn't returned. Am I right to assume that my suggested strategy wouldn't be a problem then? authData appears to be a "special" field that isn't returned if another user fetches it.
I'm slowly beginning to unerstand REST and theres one thing thats confusing me .
I understand that most of the things in REST is a "resource" . So i was wondering what kind of a resource would we be referring to in the case of a user signup / login ?
Is it users ? Then does it mean that a POST on users would signup for a new user . If that is the case , then how do i authenticate a user ? a GET on users with an encoded password / username pair?
I'm really confused with this.
I may be COMPLETELY wrong in my understanding given that i'm just starting to understand REST.
Any help is appreciated !
Thanks!
It's a bit of an unusual but common problem for REST. Keep thinking about resources.
When you login you're asking the server to create a session for you to access certain resources. So in this case the resource to create would be a session. So perhaps the url would be /api/sessions and a POST to that url with a session object (which could just be an object consisting of a username or password and perhaps the UUID) would create a session. In true REST you'd probably point to a new session at /api/sessions/{UUID} but in reality (and for security purposes) you'd probably just register a session cookie.
That's my own personal approach to login forms if I were to implement them myself but I always tend to use Spring security for that job so this requirement never really takes much consideration.
I am working on something similar and this is the solution I have taken so far. Any suggestions welcome :)
I have users exclusively for singup and account modifications.
GET /users/{id} gets a user for the profile page for instance
PUT /users creates a new user with username and password. In reality this should send an email with a link to somewhere that confirms the signup with a GET method.
POST /users/{id} modifies the user (for example change password)
DELETE /users/{id}
For authentication I tend to think that the resource I request is the token or the authentication. I have tried to avoid the word "session" because it is supposed to be anti-RESTful, but if you are just creating the illusion of an actual server-side session for your clients, I guess it is fine.
PUT /authentication/ with usename/password returns Set-Cookie with the pair user_id / hashed value. Maybe it should be POST. Not sure
DELETE /authentication/{user_id} just deletes the cookie and the user is signed out. Maybe instead of user_id it should be a unique token_id.
Resources can be created, read, update and deleted using a restful approach, see e.g.:
https://cwiki.apache.org/S2PLUGINS/restful-crud-for-html-methods.html
So if you'd like to administrate you users this would be the restful approach to do so.
If you'd like to authenticate the users which you have in your administration dataset you need
to design or select a restful authentication mechanism see e.g.
http://de.slideshare.net/sullis/oauth-and-rest-web-services
http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/
For a jumpstart on these issues you might want to check out dropwizard:
http://dropwizard.codahale.com/
A resource may have one URI or many
but One URI will have exactly one Resource
Therefore, When Authenticating a user, you are addressing a user who is already registered
While when registering, you are addressing the user (resource) which is yet to be registered.
All you need is a way to process it to your SERVER.
THIS is an example taken from SUGARCRM REST web services implementation.
REST is like http requests to your SERVER.
For eg, when implementing REST Web Services.
Every REST Request is going to same File say
www.your_domain.com/Rest.php?json={your_json_method:'method',params:'watever'}
Where in Json is the request you are sending as a parameters
Requesting to authenticate a user
{method:'SignUp', username:'abc', pass:'pass', confirm_pass:'pass'}
Requesting to register a user
{method:'Login', username:'abc', pass:'pass'}
by this way you can have as many params as you want
Remember JSON is not necessory to be used. you can use simple get params for your query