Use Microsoft OAuth to authenticate our own operations - reactjs

We have built a React web application that authenticates users with Microsoft via OAuth ( #azure/msal-browser package). Users are redirected to Microsoft auth page during signin, and then redirected back to our site once authentication has completed successfully. This is all working as expected.
However, we are in a position where we wish to add a custom PIN mechanism to protect some of our workflows. PINs will be encrypted/salted and stored in our own API, along with the existing mapping between Microsoft/Azure users and our own user state/records. In order for users to change/reset their PIN, we want to force them to reauthenticate with Microsoft before changing their PIN. I have not dealt with OAuth2 in a while, and am not entirely certain how this might be possible (since current auth workflow does not involve our server at all).
Ideally, users would navigate to the "Reset PIN" page and initiate the workflow. If possible, authentication would be handled through a pop-up (so as to not lose page state), which I think is possible as per documentation? If this is not possible, even a redirect to the same page with a specific query parameter (to indicate post-authentication) could work. The user would then be able to change/confirm their new PIN. At this point, a request would be sent to our API, including both the PIN and something from Microsoft that would allow our server to validate that the user did indeed just re-authenticate (proving they can change the PIN).
The primary sticking point is that our API must be able to verify that the user has just re-authenticated with Microsoft! One way to think about it would be that if a user gained temporary access to an unlocked authenticated workstation, they should not be able to perform restricted actions (as they do not know the PIN), nor be able to change the PIN (as they do not know the user's credentials). Yes, yes, I know physical access is effectively a compromise to any security, but this is a higher-level requirement...
I am guessing this workflow is possible, but am not certain how it might be possible. Would we use a different authentication workflow for this particular use case? Any pointers on a workflow we could investigate or use for this?

I can give you some info about how this should work. This type of flow can require a high level of extensibility in the authorization server though.
CLIENT REDIRECT
The client can send the OpenID Connect acr_values parameter, indicating the assurance levels it requires. In your case you would send two values.
CUSTOM AUTHENTICATION FACTOR
The user will perform standard authentication first, eg passwords. Then you would implement a custom second factor that uses the user identity and some custom logic to call your custom API.
ACCESS TOKENS
After authentication has completed, the authorization server will issue access tokens to the client. High privilege claims should only be included when strong authentication has been used. Some kind of scripting would be used here, that can read authentication result attributes.
API AUTHORIZATION
APIs should receive short lived access tokens. If it contains the high privilege claim, you know that your PIN requirements have been met.
CAN YOU SIMPLIFY?
It feels suspect to do your own PIN management. Can you outsource this, eg use a second factor of time based one time password (TOTP) instead? Mobile platforms such as the Google authenticator app provide tested solutions.

Related

Allow single session per user IdentityServer4

With IdentityServer4 I need to allow a single user session per time. If the user authenticates with device A and then with B, session and access token must be invalidated for A and, even better, client A could receive a notification that user has logged out in a second step.The main thing is making the server force invalidate session and token. There are similar questions, one of them redirects us to the following link:
https://github.com/IdentityServer/IdentityServer4/issues/736
where it is explained to use backchannel logout and in the login method of the identity server to obtain the previous IdentityServer sessionID that should have been persisted somewhere. Then I should send logout tokens to all clients.
Another solution is given here
How to Logout user from a particular session Identity Server 4, .Net Core?
telling us to use an ITicketStore implementation to be able to invalidate the session.
The configuration used is IdentityServer4 with authorizaton code flow with PKCE. Which approach is best for my case? Is there another approach where I could simply delete the access token in the id server database?
I think in your case probably a combination of both. In our real-world implementation we combine server-side storage of sessions via ITicketStore (stored in a custom database with sessions linked to user accounts and also storing the list of client IDs for each session) with the ability to trigger back-channel logout of any session at any time (i.e. not just via the default user-triggered mechanism).
In our case we do this to be able to invalidate sessions for other reasons (e.g. password or other security setting changes, a "log me out of everything" feature, impersonation rights being revoked etc) but this approach could form the foundation of a "single session per user" feature should you wish.

Salesforce Server-to-Server integration without any user involved

I am working on a integration with Salesforce using REST APIs and, as part of the project, I need to send updates to Salesforce and these updates are not user triggered, they are system triggered.
Because of that, what I expect to see on Salesforce Field History is not a user name but the name of our Connected App (the app that made the update).
What I see today is the user name because the way the integration was made initially using OAuth Authorization Code flow.
To change that part of the project, I followed the link (OAuth 2.0 JWT Bearer Flow for Server-to-Server Integration): https://help.salesforce.com/articleView?id=sf.remoteaccess_oauth_flows.htm&type=5
Making that, I was expeting to generate a token for a System, not for a User, but that's not what happened: when I used the token generate from the JWT Bearer Flow and ran the update, the Field History still shows the user name.
What could I do then?
Which are the options in Salesforce to achieve the behavior I'm expecting?
The most important, in my opinion, is to have a Token for our system, not for a user.
Thanks!
Everybody is an user in Salesforce. Even if you access unauthenticated pages (some contact us form? case or lead capture) - it gets tracked under special Guest User.
It sounds stupid but gives you unified interface to control permissions (Profiles/Permission sets). You want guests to access only FAQ articles and make cases? Sure thing, do it in profile, don't get paranoid about people trying to guess right URLs. You think an app was hacked? You can terminate the session just like any other "user". Want to allow login only in certain hours and from certain IP? Sure.
An app connecting with JWT will still need username (main difference being it's "just" certificate for signing the request instead of password).
Your best bet is to create dedicated "Mr System", "SystemX integration" account. It sounds like waste of license but in the long run saves you questions "why did you edit my account at 1 am" and you could even use it as backup account if you use SSO and it ever fails...

Salesforce: How to automate report extraction as JSON/CSV

I am new to Salesforce, but am an experienced developer. I am provided a link to a Salesforce report, which mostly has the right filters (query). I would like to use an REST API to pull that information as CSV or JSON so that I can do further processing on it.
Here are my questions:
Do I need special permissions to make API calls? What are they?
Do I need to create an "app" with client-key & secret? Does my admin need to grant me permission for this too?
There are a lot of REST APIs from Salesforce, which one do I need to get the info from the report? Analytics?
How do I authenticate in code?
You'd have to work with the System Administrator on the security pieces. Anybody who knows how the company works, can all users see everything, is there Single Sign-On in place, how likely is the report to change...
You will need an user account to pull the data. You need to decide if it'll be some "system account" (you know username and password and have them stored in your app) or can it run for any user in this org. It might not matter much but reports are "fun". If there will be data visibility issues 6 months from now, you'll be asked to make sure the report shows only French data to French users etc... you can make it in report filters or have multiple reports - or you can just use current users access and then it's the sysadmin that has to set the sharing rules right. (would you ever think about packaging what you did and reusing in another SF instance? Making a mobile app out of it? Things like that, they may sound stupid now but will help you decide on best path)
The user (whether it'll be system account or human) needs Profile permissions like "API Enabled" + whatever else you'd need normally ("Run Reports" etc). If you're leaning towards doing it with system user - you might want to look at Password Policies and maybe set password to Never Expires. Now this is bit dangerous so there would be other things you might want to read up about: "API only user" (can't login to website), maybe even locking down the account so it can login only from certain IP ranges or at certain times when the job's supposed to be scheduled...
Connected App and OAUth2 stuff - it's a good idea to create one, yes. Technically you don't have to, you could use SOAP API to call login, get session id... But it's bit weak, OAuth2 would give you more control over security. If you have sandboxes - there's little-known trick. You can make connected app in production (or even totally unrelated Developer Edition) and use client id & secret from it to login to sandboxes. If you create app in sandbox and you refresh it - keys stop working.
(back to security piece - in connected app you can let any user allow/deny access or sysadmin would allow only say these 3 users to connect, "pre-authorize". Could be handy)
Login - there are few REST API ways to login. Depends on your decision. if you have 1 dedicated user you'll probably go with "web server flow". I've added example https://stackoverflow.com/a/56034159/313628 if you don't have a ready SF connection library in your programming language.
If you'll let users login with their own credentials there will be typical OAuth "dance" of going to the target page (Google login, LinkedIn, Twitter...) and back to your app on success. This even works if client has Single Sign-On enabled. Or you could let people type in their username and pass into your app but that's not a great solution.
Pull the actual report already
Once you have session id. Official way would be to use Reporting API, for example https://developer.salesforce.com/docs/atlas.en-us.api_analytics.meta/api_analytics/sforce_analytics_rest_api_get_reportdata.htm
A quick & dirty and officially not supported thing is to mimic what happens when user clicks the report export in UI. Craft a GET request with right cookie and you're golden. See https://stackoverflow.com/a/57745683/313628. No idea if this will work if you went with dedicated account and "API access only" permission.

IdentityServer - separate authentification, authorization, resources without any use of UI-websites?

I want to have an architecture made up of these devices / programs with different roles (which are all separated, none of these roles must exist in one and the same instance):
CLIENT
AUTHENTICATION-SERVER
AUTHORIZATION-SERVER (there can be a multitude of these)
RESOURCE-SERVER (also many possible)
And:
I don't want to use any websites a user has to navigate to.
From my studies of OAuth and Open ID Connect I would assume that a Client could get an ID_TOKEN (after authorization) from an AUTHENTICATION-SERVER and could then request ACCESS_TOKENS to different resources from the AUTHORIZATION-SERVERs.
I guess the method I described allows identity theft on the client side.
I cant find a way to easily get id tokens from an authorization server to a client (which I could then use to authenticate to multiple authorization servers). My clients are trusted. I want to use native apps and implement open id connect among them anyway.
I do not want to use any websites as of now and still need to be able to have a secure communication with associations of claims to users and everything. Are OpenID Connect and its possible 'Flows' appropiate for this? Are there any other implementations that would allow the flow I described (or mabye derivations of OpenID Connect)?
It's not recommended but if you really don't want to use a browser (which is somewhat anti-OAuth/OIDC and is full of limitations) then ResourceOwnerPassword grant type is the one you'll need to use. This will not return an id_token though, just an access_token.
id_tokens exist to support front-channel browser-based sign in flows, they serve no purpose if using the ROP grant type.
Access tokens are issued by the authentication/secure token service (IDS4 in this case) and grant the client (optionally with user consent) access to the resource. The resource then has its own authorization rules to enforce scope and user-related ACL/business rule access controls.

Can the client modify react component state?

I'm building an admin page for an application and have a state value 'authenticated' that flips from 'false' to 'true' after a successful login (which is authenticated on the server) which then shows the actual admin panel.
Are component state values safe from tampering by the client? Basically, if the client can modify my 'authenticated' state value to 'true', they can skip the login and go straight to the admin panel (which I obviously don't want).
I read that React Dev Tools allows the client to modify values yet everyone says "validate on the server" but I am validating on the server and updating my state accordingly, if the user is approved. If it is not wise to have a state value manage this, what is the right way to conditionally show the admin page after a successful, server-side authenticated login?
I think this is an important question since tampering with state values in a React app can have huge negative consequences on data integrity within an app/database.
TL;DR: Either require an authentication token with every request or require authentication through a session.
Never trust users always. One potentially big issue is if you "hide" admin actions behind the admins page without requiring authentication.
For example, assume the backend server uses a REST API to accept commands. In the admin panel you get links to administrative actions like a button 'Delete Everything' that sends a DELETE request to server.net:8080/api/admin/everything without requiring any authentication. If you're a user, you can find that in the code potentially and then send a DELETE request to that address from anywhere without any repercussions.
We'd never give administrative privileges to anyone who would want to delete everything... Because we'll never untrust someone. Right?
Worse, someone might find the server and fuzz some inputs to it, and oops! They manage to delete everything (or even worse, GET everything stored in the database). This wouldn't be hard to do, especially if the server you use to authenticate is the same server you use to issue commands. History has proven "security through obscurity" to be a very bad paradigm. Every action should be authenticated, even if it seems like the actions will be hard to find.
Generally, providing a JSON web token or some other form of authentication token and having the user send that with every request is a good start at least, especially if it has an expiration date. The token would be provided through a separate request with valid credentials.
Sending a token with every single request obviously isn't ideal. There are a couple of other things to try. For servers using PHP, you can probably trust sessions (though very many people who know more than me would probably disagree). In more modern cases, you could try to use Web Sockets, requiring the token after connection. Then only after authentication with the token do you allow the user to make administrative requests.
That way, even if a user knows the exact command they can send to perform any action, the server won't let them without a current session or token. Unfortunately, unless you're already using Web Sockets or depending on a session, it will likely require a lot of changes. I'd consider this to be critical though.
It is always possible to tamper values in the front-end, there is no way you can rely solely on the front end to ensure security.
Your best approach is to implement some form of authentication and authorization on your backend. In this way, even is some users pretend to be admin, they will be blocked when you do the next request to the server.
Perhaps if you can send more information regarding your problem, we can think of a more specific solution.

Resources