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

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;

Related

Retrieving a trusted client token within a Keycloak addon (SPI)

As part of our effort to be GDPR compliant, I’m trying to extend our Keycloak server with some custom functionality, to be triggered when a user is removed:
sending a confirmation message to a Slack channel
sending a HTTP request to another service we built, to delete any assets that user might have created there
The first one was easy. It's implemented as a SPI event listener, following this example:
#Override
public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
keycloakSessionFactory.register(
(event) -> {
// the user is available via event.getUser()
if (event instanceof UserModel.UserRemovedEvent){
userRemovedMessageHandler.handleUserRemoveEvent(session, (UserRemovedEvent) event);
LOG.debug("User removed event happened for user: " + ((UserRemovedEvent) event).getUser().getId());
}
});
}
But I'm stuck on the second task (sending the HTTP request). The other service is set up to require a trusted client token, using a designated Keycloak Client and scope. The equivalent POST request to auth/realms/{realm}/protocol/openid-connect/token is done with these parameters:
grant_type: password
username: {user that is about to be deleted}
password: {user password}
client_id: {the specific client}
client_secret: {that client's secret}
scope: usersets
I can’t find out how to do that from within the SPI code. I can retrieve a regular Access Token (as described in this other SO post):
private String getAccessToken(KeycloakSession session, UserModel deleteUser) {
KeycloakContext keycloakContext = session.getContext();
AccessToken token = new AccessToken();
token.subject(deleteUser.getId());
token.issuer(Urls.realmIssuer(keycloakContext.getUri().getBaseUri(), keycloakContext.getRealm().getName()));
token.issuedNow();
token.expiration((int) (token.getIat() + 60L)); //Lifetime of 60 seconds
KeyWrapper key = session.keys().getActiveKey(keycloakContext.getRealm(), KeyUse.SIG, "RS256");
return new JWSBuilder().kid(key.getKid()).type("JWT").jsonContent(token).sign(
new AsymmetricSignatureSignerContext(key));
}
but that's not the token I need. In order to retrieve the required trusted client token I’d need the internal Keycloak / SPI-equivalent of the above POST request, but I can’t figure out how to do that or where to look.
Besides, I'm not sure if it is even possible to retrieve a trusted client token for a user that is in the process of being deleted. In other words, I don’t know if the UserRemovedEvent is fired before, during or after the user is actually deleted, or if Keycloak waits until any custom EventHandlers have finished running.
(In order to figure that out, I'll try if it is possible to retrieve that token using a regular http POST request from within the add-on code, but I have no idea if it's possible to connect to Keycloak like that from inside. I'll update the question if that works.)
I'd greatly appreciate any suggestion!
(I also asked this in the Keycloak discourse group here but it looks as if it will remain unanswered there)

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.

Is there a way to revoke another user's access tokens and end their session in Identity Server 4?

Is there a recommended way to revoke another user's access in Identity Server 4? The use case I'm looking at is an Administrator revoking system access for a currently logged in user.
I've read the documentation for the Revocation Endpoint and can see how that can be used by a user to revoke their own access. But how can this be done when the Administrator wouldn't know what a particular user's access token is?
Same goes for the End Session Endpoint I suppose, how would the Admin know their ID Token?
What I've tried so far is implementing an IProfileService and checking the user's account is valid in the IsActiveAsync method. In our customer db I can deactivate their account and this has the desired effect of redirecting them to to the Login page. But the tokens and session are still 'alive'. Would this be a good place to end session and revoke access token?
Or is persisting user tokens to the database an option?
Update
Based on the answer from #Mashton below I found an example of how to implement persistence in the Identity Server docs here.
Creating the data migrations described there will persist tokens to [dbo].[PersistedGrants] in the Key column. I was confused at first since they didn't look anything like my reference access tokens but after a little digging I found that they are stored as a SHA-256 hash. Looking at the DefaultGrantStore implementation in Identity Server's GitHub the Hashed Key is calculated as follows ...
const string KeySeparator = ":";
protected string GetHashedKey(string value)
{
return (value + KeySeparator + _grantType).Sha256();
}
... where the value is the token and the _grantType is one of the following ...
public static class PersistedGrantTypes
{
public const string AuthorizationCode = "authorization_code";
public const string ReferenceToken = "reference_token";
public const string RefreshToken = "refresh_token";
public const string UserConsent = "user_consent";
}
Using persisted grants doesn't give me the original access token but it does allow me the ability to revoke access tokens since the [dbo].[PersistedGrants] table has the SubjectId.
Update 2 - Identity Server keeps creating tokens
I created an implicit mvc client and after successful login I'm dumpimg the claims on the screen. I delete the access token from the persisted grant db then use Postman to end the session in the End Session Endpoint (using the id token in the claims). When I refresh the browser I'd expect the user to get redirected to the login screen but instead they get a new access token and a new id token. The Client.IdentityTokenLifetime is only 30 seconds.
Any ideas of what I'm missing here?
You can only revoke Reference tokens not JWTs, and yes those need to be stored in a db. Have a look at the IPersistedGrantStore (of the top of my head, so may have got the name wrong), and you'll see the structure is pretty simple.
Once you've got them stored, you can obviously do anything you like admin-wise, such as change the expiry or just outright delete them.

Silent login using Username in Azure Active Directory in Xamarin

I am developing app using Xamarin Forms. I have created a directory on azure portal. As i see references over internet , active directory authentication uses Microsofts login page to log in.
I want to create native login form and pass user name to active directory and authenticate it.
Is it possible to pass user credentials programatically and authenticate user?
How can i pass user credentials?
I have tried following but i got "(411) Length required" exception
var request = WebRequest.Create(string.Format(#"https://login.microsoftonline.com/{0}/oauth2/token?client_id=5e811f4f-4fa4-451e-a439-ca05cabc02d7&grant_type=password&username=02atul.com#gmail.com&password=userpassword&scope=openid", tenant));
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
using (HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
Debug.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
if (string.IsNullOrWhiteSpace(content))
{
Debug.WriteLine("Response contained empty body...");
}
else {
Debug.WriteLine("Response Body: \r\n {0}", content);
}
}
}
My username is my email id. Is it correct user name? As i am getting bad request error now. What's missing?
Technically you could use username and password flow which is described in more detail here with ADAL.
However, you need to revisit your scenario and understand if it really accomplishes what the Azure Active Directory Platform is for.
Users use OAuth2 based authentication to get the security and safety of only having to share their passwords with trusted identity providers like MS, FB, Google, etc... In general, the safety conscious person will NOT want to type in a password into your random website, and trust that you do not abuse that information. If you want to use AAD, you should also use our login experiences, as this is really what the customer is paying for in our service in many regards.
EDIT: If ADAL no longer supports this flow, you can simply generate the http requests yourself:
POST: https://login.microsoftonline.com/xxxxx.onmicrosoft.com/oauth2/token
Content-Type: application/x-www-form-urlencoded
resource={resource}&client_id={clientId}&grant_type=password&username={userName}&password={password}&scope=openid&client_secret={clientSecret}

Where does request.user come from in Parse Server

I'm trying to port my Parse.com app to Parse server.
According to the Parse migration guide, Parse.User.current() can no longer be used and instead you should fetch the current user via 'request.user'.
However, request.user is always undefined for me.
For example, when I successfully login a user and then redirect to another path (/mypath), the incoming request at mypath does not contain a user object.
Parse.User.logIn(username, password, {
success: function(user) {
res.redirect('/mypath');
}
})
// Index of the /mypath controller
exports.index = function(request, response) {
// request.user is undefined here
}
How do I work with the active user after I logged in sucessfully?
I think I figured it out.
When working in Cloud Code there is no magic request.user. Rather when logging in you need to manually store the user info in the current session. In the Parse.com days this would've been managed by the parse-express-cookie-session middleware. This is explained in the cloud code guide. With Parse-Server you can insert your own middleware to manage the user as is described here.
However, the request.user will be available when receiving requests from a client SDK using an authenticated user.

Resources