Can the Structure of a React App be hidden behind a login? - reactjs

Our React App is secured by a login. This prevents attackers from accessing user-specific content because they are unable to begin a session without credentials.
We are now ready to start testing with real world users. For this we would like to host our app on a publicly accessible endpoint. But we cannot allow unauthorized individuals to explore our app. Of course they would not get to see user data but they could potentially view our apps structure and features and steal our product, correct?
Is there a way to prevent this or should we instead only allow access from a VPN for the initial test phase?

React is a bundled application. That means your entire react app is in a single javascript file. Even if you had a login on the react app, someone could still just... bypass it.
A way to prevent this would probably be just a simple nodejs server which has a login:
app.get('/secretapp/:password', (Req,Res) => {
if (Req.params.password == "hunter2") {
Res.send("react_app.js")
} else {
Res.status(404).end() // What route? Nothing to see here. Go away.
}
})
Otherwise, a VPN is not a bad choice.

Related

Custom React GUI for oidc-client-js

is there a way to user your custom React GUI with oidc-client-js? I know that if you trigger authentication endpoint using:
// PopUps might be blocked by the user, fallback to redirect
try {
await this.userManager.signinRedirect(this.createArguments(state)); //Shows midleware login form
return this.redirect();
} catch (redirectError) {
console.log("Redirect authentication error: ", redirectError);
return this.error(redirectError);
}
Middleware will try to render its predefined login form:
However I have my own React form and I only need to pass to OICDClient params (email,password) and get back User instance to display UserName etc. Something like:
var loggedUser = await this.userManager.signinCustom(state.loginEmail, state.LoginPassword); //Login using credentials
I don't want to write all the logic by myself I really want to use all functionality from OIDCClient - only with my GUI (loginForm, registerForm, updateUserForm etc).
I'm using scaffolded library from MSDN using command:
dotnet new react -o <output_directory_name> -au Individual
Is there any method/implementation to initialise oidc-client-js from React components and not user default GUI forms?
Thanks a lot!
I might be missing some thing but the whole idea of using a 3rd partly federated auth provider be it your own/your company's SSO (say developed using Identity Server 4) or say Google sign in(say using their Firebase JS kit) or Microsoft sign in, Facebook sign in etc. is that you will be redirected to their authentication endpoint where you then use say your google credentials (if you are using google sign in for example) to sign on to google auth servers. Once you do that then a payload (consisting of an identity token and access token) is returned back to your redirect URL which you must configure for your specific app.
By saying you'd like to provide your own sign-in form you are missing the entire point of using a 3rd party authentication provider. Besides, you/your app shouldn't know about user names and passwords and you don't want to have access to all that information. All that you should be interested in knowing whether the user, who are using one of the federated authentication providers, that you would have configured for your app, are who they claim to be and you then delegate all that heavy lifting to your 3rd party authentication provider.
Besides, say your company has a SSO system of their own then all your company's app will use that SSO system and from a UI perspective you want to show the same login screen so as to give a consistent user experience.
In addition, if you show me a google authentication button and then on clicking that button you show me some weird form that doesn't look like the typical google picklist sign-in form then I'll shut down your app before you can say hello, and, I suspect most user would (and should) do the same. The reason being that when you show me a google sign-in page then I know that you/your app will only get back what you need and I wouldn't ever entrust my google user name and password to any other application.
If you really want to have your own authentication form then you'll have to manage user names and passwords yourself, the way we used to do things probably over 10+ years back.
If you decide to go the route of setting up your own authentication system and then say use identity server 4 then yes you can certainly change the UI and customize it to your heart's content, but that will happen at the server level, not at the level of your react app. Point being that in any SSO system the user will be redirected to the that auth provider's endpoint where they then authenticate (and, optionally, provider permission for your app to use certain claims) and once they do that they they are redirected back to your redirect endpoint with a payload (JWT token).
Lastly, even if you could some how wire up a client side sign in form, I'm not sure you would want to use it. Passing passwords & user names over the wire isn't a good idea. You'll always want to use a server rendered sign in form.

How to implement optional authentication in React/Express app?

I'm building self-hosted bookmarks manager app. I'm using React on frontend and Express on backend. I have already implemented normal authentication system based on JWT. But now I want to make authentication optional. Beacuse it's self-hosted app some users might want to use it only on local network = auth disabled, and other expose it to the Internet = auth enabled.
On backend, I'm using env variable USE_AUTH=false to disable authentication and everything works as it should.
On frontend however, I'm rendering some components and routes based on isAuthenticated value stored in AuthContext state. It is set to true on successful login/sign up and to false on logout.
My question is how can I tell my React frontend that I'm not using auth on backend so it can render all hidden components, don't include token to requests etc.?
The only solution I came up with, is to send request when user opens the app to ask server if it's using authentication. So something like GET /api/auth/ping will return { useAuth: true } or { useAuth: false } depending on USE_AUTH value.
But I don't know if it is as optimal solution. Is there a better option / pattern I can use?
Thanks
I believe the correct way to do this is to specify in your react app, which routes are private and which ones are public, depending on that, components will be rendered.

Should I be testing for an AdalException inside an ActionFilter?

In some Azure Samples on GitHub like this one we have an example of using ADAL to access a protected Web API resource. The effort is protected by a try/catch looking for an AdalException
I'll summarize the code as thus:
try
{
//pseudo code... configure a client to use an access token..
var token = ADAL.AcquireTokenAsync...
var httpClient = new HttpClient(baseUri, token);
// use the token for querying some protected API
var result = //await HttpClient to return data...
return View(result);
}
catch (AdalException)
{
// do something important with the exception,
// e.g. return an error View w/login link
}
So, as I start to flesh out my MVC controllers to use ADAL-access_token'd requests, do I really want all this try/catch business in every controller?
Does it make sense to create an ActionFilter? This snippet was inspired by code I saw at this Azure Sample
public class AdalErrorAttribute : FilterAttribute, IExceptionFilter
{
void IExceptionFilter.OnException(ExceptionContext filterContext)
{
if(filterContext.Exception is AdalException)
{
if (filterContext.RequestContext.HttpContext.Request.QueryString["reauth"] == "True")
{
//
// Send an OpenID Connect sign-in request to get a new set of tokens.
// If the user still has a valid session with Azure AD, they will not be prompted for their credentials.
// The OpenID Connect middleware will return to this controller after the sign-in response has been handled.
//
filterContext.RequestContext.HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties(),
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
}
}
My Context:
I'm taking a rather homogenous set of scaffolded MVC controllers that were EntityFramework-centric at the time they were generated.. but now need to be re-tooled to access my Web API (via my new AutoRest client)
#vibronet made a good point - don't do that for your WebAPI endpoints. They're called with bearer auth, and shouldn't be automatically redirected to a sign-in process. Give back a 401 indicating the credentials are invalid and let it go.
But for an MVC app the user is using interactively, this is a reasonable idea to follow, subject to a couple of constraints.
Make sure your filter is very tight about what it matches. Ie. make sure it's only handling exceptions you can reasonably-certainly draw to an authentication issue (vs. being an access-denied issue). Perhaps a custom exception that gets thrown by your wrapping logic, or some extra conditions beyond it being an ADALException to make sure it's an issue solveable by logging in again.
Unless you really want every request have this handling, look at attaching it at the controller or action layer instead of globally.
Look out for potential "loop" issues, where you get an error, tell the user to log in again, get the error, make them log in again, etc. Maybe set some data in the session when triggering the login, or in the login process, or something like that. Better to give an error message than get the user's browser stuck in an infinite-redirect loop. (of course, this applies to the manually-handle-the-exception case too)
Hope that helps.
There are two issues with the approach.
the sample code you mention and the filter implementation are for web APPs, not web API. That's an important difference. Web APPs can rely on the browser to interact with the user for authentication; web APIs don't know what UX the client has (or even if there's any UX at all) hence the error behavior is radically different
a controller might call multiple APIs, requiring multiple token requests for different resources- which can fail or succeed independently, and connect to different providers. Different controllers might call different resources. The reaction to the error might vary. A filter such as the one you prototyped would only help with a very narrow set of cases.
Hence I would say that unless your controllers are all expected to perform very homogeneous logic, a filter such as the above would not give you the flexibility you need.

Incorporate Keycloak login into SPA

We're currently evaluating Keycloak as our SSO solution and while it works for our servlet-based applications there's a question regarding our (React-based) SPAs.
What our designers want: as an example let's say we have an email client spa. The user is in the process of writing an email but then gets distracted. When he returns the SSO session has already timed out and a re-login is required. The user should now be presented with a login form and after login it should be possible to send the email that's still in the SPA's local storage (i.e. re-login without restarting the SPA or losing data).
AFAIK Keycloak doesn't provide an authentication-api (for good reasons) and uses a redirect to the login page and back to the application (as I understand it for mobile apps the system browser would be used). If I'm not mistaken that redirect would then mean the SPA is then reinitialized and thus the data would be lost.
So here's the question: is what our designers want possible to do with Keycloak?
If yes, how would it be done? Directly posting to the login-url that Keycloak is using seems like a bad idea since the tokens would probably not be stored correctly and there might be same-origin policy problems. Would doing it inside an iframe or popup-window work?
For someone who comes back to this question,
I think it's better to stick to the best practice for oAuth2/OpenId Connect for SPAs which is currently "Authorization Code Flow" with PKCE.
https://oauth.net/2/pkce/
https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-13
A normal flow here needs a complete redirect to the auth server and back so your app will completely re-initialize. Or you use check-sso like Sébastien already mentioned with silent mode.
https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/javascript-adapter.adoc
You can configure a silent check-sso option. With this feature enabled, your browser won’t do a full redirect to the {project_name} server and back to your application, but this action will be performed in a hidden iframe, so your application resources only need to be loaded and parsed once by the browser when the app is initialized and not again after the redirect back from {project_name} to your app. This is particularly useful in case of SPAs (Single Page Applications).
This way the login will happen in an iframe and the app initializes only once and should preserve state.
Even if it's not considered as a best practice you can turn on Direct Grant Access for your client which enables to login through a REST call.
Anyway, about not loosing the state of your app, this is a bit outside the scope of Keycloak but you should be able to achieve that with having the state in your redirect URL for instance ?
Also, if you don't want your app to automatically reidrects to the login page you can use : keycloak.init({ onLoad: 'check-sso' }) instead of login-required

Using Multiple Angular App and Session Management

I have 4 angular applications one is a landing app which asks user to login and has to redirect the user according to its type
to one of the other 3 applications. I am unable to figure how to should i achieve that.
Have the three apps running on different subdomains. Upon login backend send a redirect response, figuring out what type of user it is?
But this leads to cors Error. Also i am not sure whether the cookie which i am setting will be accessible in all the subdomains or not.
Is there a way out?
You can do a redirect, but it seems like an unnecessary step (and kind of convoluted for this type of application).
Instead of returning a redirect based on login, it seems more straightforward to just return the address you want to redirect to in the response. Trigger a lookup to determine which app you should be directing to (however you're doing that) and then return the address of the app in the response data. From within Angular, you can extract the address from within response.data in $http. (see angular docs). The nice thing here is you also keep routing control and knowledge of state within Angular itself.
As for the apps themselves--instead of a subdomain, you can simply put the apps into different folders on your domain. This deals with CORS and the cookie issue.
Otherwise, you'd need to set a CORS header. You would do this on whatever backend you're sending the requests to--there's usually some sort of library to make it easy, for example, Flask CORS for Flask. If you need to share cookies in this case, this StackOverflow answer discusses one way of doing it (using an intermediary domain).
Generate a security key for the user session with some TTL in an authentication table when you authenticate the user with your App1
Redirect the user to any other app in any domain with this security key where they can query the authentication table and verify the user.
Let these other applications work on their own (in the front end) and communicate with the back-end with the security key when necessary.
Lot of PHP frameworks has built-in support for this mechanism. My favorite is Silex.

Resources