StrapiJs- User authentication and user profile - social-networking

1) I built profile information content type in strapijs. Added user relation to it. Profile has and belongs to one user". Like a simple social media.
2)I added couple users/profile information. Connected it for spesific user.
3) Tested api with postman. Authentication works i can see profile information. But the problem is, when a user authenticated could access other users profile information.
How can i restrict one user to see only related profile?
Thank you

Okay so for your use case you have to restrict GET /users/ to all your users.
With that, no one will access to other data users.
But you can access to GET /users/me/ route. And you will be able to access to the Auth User data.

You can easily create custom policy which will compare authenticated user.id and requested profile user id and respond only if they are the same - read about creating policy here - Strapi Policy Help
module.exports = async (ctx, next) => {
const { body } = ctx.request
if (ctx.state.user.id == ctx.request.query.userid || ctx.state.user.id == body.userid) {
return await next();
}
ctx.unauthorized(`You're not allowed to perform this action!`);
};
Note that you have to insert userid field to the profile model. I had to use logical OR because I have different types of requests - it depends on your request only..

Related

Create new Parse.Session programmatically for a user, without their password

I'm working on a existing Parse server, and I am currently adding an OAuth system, so that external apps can connect in the name of Parse.User.
I created different classes for codes and tokens, and now my external apps can send requests with an accessToken, corresponding to their application and user (who granted access).
I'm looking for a way to inform the Parse server that the "logged in user" in requests is the end user that authorized the OAuth application. For this, I have created an express middleware handling request before the Parse server middleware, extracting the access token from the request, getting the correct User and Application, and then I wanted to create a Parse.Session programmatically, get the token, and set it in the request as x-parse-session-token. This way, the next handler, Parse, would treat the request as authenticated and performed by the end user.
My problem here is that I cannot find a way to create a session programmatically, I'm aware of the Parse.User.logIn, but that works only with a password.
I've tried the following:
const oAuthSession = await new Parse.Session().save({
user: user // user got from Parse.Query(Parse.User) with masterKey
}, { useMasterKey: true })
But get a Cannot modify readonly attribute user error.
Any hidden method to programmatically create a Parse.Session without a password ?
As pointed out by #DaviMacêdo in the community forum: https://community.parseplatform.org/t/create-new-parse-session-programmatically-for-a-user-without-their-password/1751
We can inject the user directly in the request field, and it will be picked up by Parse: https://github.com/parse-community/parse-server/blob/f6a41729a7a3adc6bd5310cefb3458835b4abb58/src/middlewares.js#L199
const user = await new Parse.Query(Parse.User).get(‘idOfUser’);
req.userFromJWT = user;

Keycloak: API to check if user token has roles for a particular resource

I have a keycloak server setup. I am using the token endpoint: http://localhost:8080/auth/realms/demo/protocol/openid-connect/token to authenticate a user and generate a token. This token I understand can be used in subsequent calls to verify if it is a valid user.
However, I am not sure how do I use this to authorize the user? ie verify if this user has the roles to access a resource.
I see that it is possible to configure a resource URI under the client section. But once that is done, I want to be able to read the token and verify if this user has the roles to access this resource.
Right now, this is what I am doing:
I have used spring boot here.
doSomething(String token)
{
1. get token info using: http://localhost:8080/auth/realms/demo/protocol/openid-connect/userinfo
2. from this get the roles the user has
3. Manually check the roles required for the above function. (Right now, this is set in a simple switch statement)
4. If the role obtained from step 2 matches what we get in step 3, go ahead. Else return failure.
}
I want to know if step 3 above can be done in a better way. I know taht you can set a resource in clients from the keycloak console. What I was hoping is we could replace the 4 steps above with something like:
keycloakAPIToAuthorizeToken(token,resource)
which would tell me whether this user has the roles (obtained from token) to access this resource.
Please suggest if this is doable.
Thanks in advance.
Om
There are MANY ways to do this. One of them is to use Keycloak's roles, and assign those roles to users. That way, in your server/api you can check if the user has that role and proceed or reject the call.
Example, in my api project, I have some endpoints that are exclusive for system administrators, so I have a role SystemAdministrators:
and then, if you go to Users, Role Mappings, you can add that role to the users to set as admins:
Then, any api call should include a bearer token (obtained from the Keycloak login), in your code, you can decode this jwt (it will depend what language you are using, I use python so its pretty simple), it will have an element "realm_access" and inside it, a "role" element, example:
"realm_access": {
"roles": [
"offline_access",
"uma_authorization",
"SystemAdministrators"
]
},
If that element contains the role SystemAdministrators, you know is a system administrator.
I found this is the simplest way to do it. You can get fancy and use role attributes to determine individual options inside screens, etc (attributes are key/value pairs so the way I implemented this is, the key is an option name and the value is the permission level, for example: "screen1": "read", "screen2": "write", and so on. For this, you would need to use the keycloak admin api: https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_roles_resource which has many endpoints that can help you.

Mysterious users in my database that didn't come from my registration process

I have a Laravel-5.5 application in development with a live test application exposed on Google App Engine. My registration process includes the standard Auth registration from Laravel. The RegisterController then redirects to a profile page if there isn't one for the user already.
public function redirectTo()
{
if (!Auth::user()->profile)
{
return '/profile';
}
else
{
return $this->redirectTo;
}
}
The profile controller creates a new userprofile record for the user automatically as the page loads.
$(document).ready(function ()
{
...
getProfileData(profileId);
...
});
getProfileData() posts to the controller. If ProfileId is empty, the controller creates a new record and sends a verification email to the registered address.
How can a user be created without then being redirected and a profile being created?
Users are being created on the live site without profiles or sent verification emails. The user_agent in the session records for these users appear to be real.
Any ideas about how these users are being created and how to stop it would be most helpful.
I believe that Laravel is actively being attacked by actors that are seeking sites with poor security practices. It starts with visiting the site and getting an active session, Then harvesting the sessions csrf-token and using the token in a non site generated post (crawler?) to the standard Laravel registration route.
Since my site has a two part registration that generates a profile and the profile needs to be verified by a human before access is granted, registering and then ignoring the response's redirect to the profile page gets the partially completed registration.
To stop the resulting database clutter in the users table I changed both the standard authentication routes and the expected fields that are returned from the registration form.
Since these changes I have had no half registered users show up in the database. I'll update this answer if I ever see more of this activity.

How to know if a given user is already logged in with MSAL?

With msal.js library (The Microsoft Authentication Library), which is the way to know if a given user is already logged in? My intention is to avoid to show login pop-up if the user's credentials are already saved in browser's storage
My current approach:
function isUserLoggedIn(username) {
const agent = msal.UserAgentApplication(...);
const user = agent.getUser();
return user != null && user.displayableId === username);
}
But I'm not sure if I have to check if the user credentials are outdated/expired. Which is the proper way to go?
With the MSAL agent instance, you can get user information because it is cached. Getting information (such as the userId) doesn't mean that the user's credentials are still valid (logged in).
To be 100% sure that the user is logged in, ask for a token
const promise = agent.acquireTokenSilent(...)
If the user is not logged in, the promise will be rejected with the error code user_login_error
If, on the other hand, the user is still logged in, the promise will be resolved
From MSAL samples, they were checking this way:
let isLoggedIn = this.authService.instance.getAllAccounts().length > 0;

How do I use password reset REST APIs on service now?

I want to simulate the password reset service for service now users from an external application and I have installed Password Reset - Orchestration Add-on plugin on my servicenow developer instance. Along with this I can see a list of Pwd Reset APIs on my REST explorer (e.g pwd_init, pwd_verify, etc). I went through the documentation available on this documentation page but I'm at a loss to understand what the request payload would be like if I'm trying to call these APIs from an external service like Postman. I wanted something similar this api documentation.
Can anyone help me with this?
Use the Table APIs to do this.
In order to reset a user's password, you basically want to update the user_password field of the user record from sys_user table.
Method: PUT/PATCH
http://<instance>/api/now/table/{tableName}/{sys_id}
here tableName will be sys_user and sys_id will be the sys_id of the user's record in sys_user table.
The body of the API request should be something like this:
{
"user_password": "resetpasswordtext"
}
Bear in mind that this will reset the user's password but the new password will not be "resetpasswordtext". So the user will not be able to login using "resetpasswordtext".
To actually set the password for a user via API, same table API as above can be used. But in order to store the password properly encrypted in the database, below query parameter should be added in the request URL to set the password.
sysparm_input_display_value=true
So the API call will be
Method: PUT/PATCH
http://<instance>/api/now/table/{tableName}/{sys_id}?sysparm_input_display_value=true
BODY: {
"user_password": "newpassword"
}
Now the text "newpassword" can be used by the user to login to the instance.
hope it helps in your use case.
so, my use case did not involve using the Password reset API, but for those of you interested in generating a new password externally, then making an api call to set that as the new password for that user, then here is acode sample that is based on Milind's answer above:
Python3
def change_password_snow(user, pwd, new_pwd, snow_url, sys_id):
# Set the request parameters
url = snow_url + sys_id
# Set proper headers
headers = {"Content-Type":"application/xml","Accept":"application/json"}
# Set query params
params = {"sysparm_input_display_value": "true", "sysparm_fields": "user_password"}
# Do the HTTP request
response = requests.patch(url, auth=(user, pwd), headers=headers, params=params, data=f"<request><entry><user_password>{new_pwd}</user_password></entry></request>")
return response
Setup on ServiceNow
For this to work, the user you are authenticating with in ServiceNow needs to have Admin privileges.
Either that, or modify the sys_user.user_password ACLs to allow non admin users to read and write to that field if they have a role that you select. For my use case, I created a custom role and attached it to that user.

Resources