Intermittent problem using loginPopup MSAL JS in a REACT - reactjs

I'm using MSAL JS in order to authenticate users in react application developed using REACT. Sometimes login works perfectly, redirecting users to the home page of the app. Other times login popup opens, users insert their login but then login procedure fails with this error:
hash_empty_error: Hash value cannot be processed because it is empty.
Please verify that your redirectUri is not clearing the hash.
I know this issue was raised before but never seen proper solution how to overcome this error

What worked for me was to set the redirectUri to a blank page or a page that does not implement MSAL. If your application is only using popup and silent APIs you can set this on the PublicClientApplication config like below:
export const msalConfig = {
auth: {
clientId: process.env.REACT_APP_CLIENTID,
authority: `https://login.microsoftonline.com/${process.env.REACT_APP_TENANTID}`,
redirectUri: 'http://localhost:3000/blank.html'
},
cache: {
cacheLocation: "localStorage"
}
}
If your application also needs to support redirect APIs you can set the redirectUri on a per request basis:
msalInstance.loginPopup({
redirectUri: "http://localhost:3000/blank.html"
})

Related

MSAL + Capacitor integration React

I'm trying to integrate MSAL authentication in my app but i can't find a solution to solve the redirectUri value.
This is my config file:
export const msalConfig = {
auth: {
clientId: bundle id,
authority: https://login.microsoftonline.com/{your tenant ID})
redirectUri: ???,
},
cache: {
cacheLocation: 'sessionStorage', // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
},
};
The app, being a web app, when used on iOS has capacitor://localhost as link so I don't know how to configure it on Azure AD portal.Tried to put capacitor://localhostas redirectUri and different approaches that i found on internet but none of them work.
Redirect URL should follow the format msauth.[Your_Bundle_Id]://auth. For more information take a look to MSAL redirect URI format requirements.

How to use Amplify for implicit federated oauth w/ cognito *without* hosted UI

(Due to the confusion with what is amplify I'm borrowing the below from another post)
I want to use the feature federation of a user pool NOT an identity pool
I do NOT
want to use this feature with the hosted UI.
I do NOT want to use
this feature with identity pool.
I do NOT want to use this with
the amplify CLI or amplify aws service
I am using aws-amplify https://www.npmjs.com/package/aws-amplify and the UI elements from #aws-amplify/ui-react and #aws-amplify/ui-components.
I am using v1.2.26 of the #aws-amplify/ui-react
The webapp is a react app that runs on our own backend in elastic beanstalk.
We have a web application that uses cognito user pools and user/password to authenticate into our application and we would like to add the ability to use SSO starting off with Google and eventually implementing other SAML idp's.
Our ideal flow is for a user to click "Sign in with google" on our page and be immediately taken to a google login screen that once authenticated brings them into the application with a refresh token so that their user session can be persisted.
Currently getting the error No Cognito Federated Identity pool provided
Is there a way to do this or should we implement the auth flow manually? Amplify seems to be a mess unless you use it exactly as they want with the hosted UI and underlying amplify service.
// Amplify Configuration
import { Amplify } from 'aws-amplify';
useEffect(() => {
//Helpful for debugging
Hub.listen(/.*/, info => console.debug(info));
}, []);
Amplify.configure({
Auth: {
region: pool.region,
userPoolId: pool.id,
userPoolWebClientId: pool.client,
storage: window.sessionStorage,
mandatorySignIn: false,
},
oauth: {
domain: `myCognitoUserPoolTest.${pool.region}.amazoncognito.com`,
//We don't have a sign-in or sign-out URL because its handled by Cognito right now
redirectSignIn: 'http://localhost:3000',
redirectSignOut: 'http://localhost:3000',
// This is the default don't need it explicitly set
// Setting this to "token" only returns a short-lived access token not a refresh token
// responseType: 'code',
},
});
return (<AmplifyAuthenticator
federated={{
googleClientId:'myGoogleClientID',
//Put this in here to just to see
oauthConfig: {
domain: '-domain-.auth.-region-.amazoncognito.com',
//We don't have a sign-in or sign-out URL because its handled by Cognito right now
redirectSignIn: 'http://localhost:3000',
redirectSignOut: 'http://localhost:3000',
scope: ['email', 'profile', 'openid'],
},
}}
>
<AmplifyGoogleButton
slot="federated-buttons"
clientId="myGoogleClientID"
//Tried calling this explicitly, same result.
onClick={() =>
Auth.federatedSignIn({
provider: CognitoHostedUIIdentityProvider.Google,
}).then(res => alert(res))
}
/>
</AmplifyAuthenticator>);

In MERN how do I manage JWT cookies client-side?

Can anybody explain to me how to use cookies with the MERN stack? I'm trying to make an analog for social media. I understand first steps but then - I don't know how to proceed.
I create endpoints for login, register, logout.
I create react app and made register form, login form. And from there - I'm sending axios request to my express endpoints.
In express - I'm saving to MongoDB user or checking login + password from DB.
I'm creating a JWT token with id payload and sending it via cookie.
But what do I do next? How to save which user is logged in in the react app? And how do I check if the user is still logged in and the cookie exists? Thanks for any help!
I have a repo that does much of what you are trying to do. In a React component called SignInForm.js I authenticate the user using a POST request. If the authentication is successful the server sets a cookie called 'token' and the client then redirects to a component called Dashboard.js. In Dashboard.js I have, among other things, the following lines of code:
import {useCookies} from 'react-cookie'
export const AppContext = createContext()
export default function DashBoard() {
const [cookies, setCookie, removeCookie] = useCookies(['token'])
let [authenticated, setAuthenticated] = useState(cookies.token !== undefined)
return (
<AppContext.Provider value={{authenticated, setAuthenticated}}>
The above code looks for the cookie called 'token'. If it exists then it sets authenticated to true and loads it into the AppContext provider. The server API routes, of course, are protected server side. But client side I can use the value of authenticated in other components to allow or disallow access. For example in a component called UserForm.js, I check authenticated like so (some code removed for clarity):
import { AppContext } from '../DashBoard'
export default function UserForm(){
let { authenticated, setAuthenticated} = useContext(AppContext)
if(!authenticated){
document.location = '/signin'
return <></>
}
and if they aren't authenticated then I redirect them to the SignIn.js component.
When you hit the backend route in which you are sending the token, on success the token will be automatically set in the browser. You can check by opening cookies in the Networks tab in the browser. But in order to do so you need to set the proxy.
In the frontend package.json file add:
"proxy": "http://localhost:{your-port-number}/"
Now cookie will be set automatically.
Now in order to authenticate user, you need to send this token to backend from frontend. In order to do so whenever you make a request to backend do the following(example):
axios.get('userdashboard', { withCredentials: true })
This will make sure that cookies will be used. (In this case 'userdashboard' is the route which only logged in user can access.)

Redirect loop on authentication after token expiry using react-aad-msal

I've attempted to replace ADAL with MSAL. The first time the user accesses the app it authenticates correctly, but I am getting into a situation where our app goes into a redirect loop after a period of time. I presume this is when the token expires (1 hour by default, I believe). This happens sometimes when the app is idle after an hour, which is why I think it may be to do with the way a new token is obtained as well as when I refresh the browser window.
** AuthProvider.ts **
import { Configuration } from 'msal
// Msal Configurations
const config = {
auth: {
authority: 'https://login.microsoftonline.com/' + process.env.REACT_APP_AAD_TENANT,
clientId: process.env.REACT_APP_AAD_CLIENT_ID,
postLogoutRedirectUri: window.location.origin,
redirectUri: window.location.origin,
validateAuthority: true,
navigateToLoginRequestUrl: false,
},
cache: {
cacheLocation: 'sessionStorage', // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
},
} as Configuration
// Authentication Parameters
const authenticationParameters = {
scopes: ['openid', 'api://' + process.env.REACT_APP_AAD_SCOPES],
}
// Options
const options = {
loginType: LoginType.Redirect,
tokenRefreshUri: window.location.origin + '/auth.html',
}
export const authProvider = new MsalAuthProvider(config, authenticationParameters, options)
then when calling our api, I thought I could call the getAccessToken method like below and I would silently receive a valid token, but I just end up in a redirect loop.
I'm wondering why it works the first time the user accesses the app, but not when trying to refresh the token. I would think the Azure config is correct, since it works the first time. Could it be an iframe / browser issue or is it a code problem?
[![IdResponse tokenType undefined][1]][1]
[1]: https://i.stack.imgur.com/6Vkwn.png
To Acquire a valid token using refresh token you need to call msal.js token API acquireTokenSilent. For more information on how to get acquire a token using msal.js refer this document.
You can refer the msal.js sample for your scenario.

Authenticating to Asp.Net core 2.0 MVC application (cookie) and REST API (JWT) simultaneously

I am using the VS 2017 Asp.Net Core 2.0 web application template, with the React.js template option.
This gives me a basic MVC + React setup with:
1. a single MVC page that hosts the React application
2. A ClientApp folder for the React application
3. A Rest API to which the React application talks.
By default none of these are authenticated. I want to secure both the MVC page and the REST API.
I have modified the code so that I use cookie authentication for the MVC page, and JWT bearer authentication for the REST API.
For the MVC cookie authentication, I have an MVC login login form. This works fine.
For the REST API authentication, I have added a TokensController that receives a username and login, and generates a JWT token to be used for requests to the REST API. This also works fine when tested from Postman: if user If user tries to access REST API without valid JWT token, user gets 401 response. If users gets token from TokensController and uses that to access REST API, user gets 200 response.
My problem is combining these two. What I would like is that when an unauthenticated user comes to my MVC page, user is redirected to a login form. When submitting that form, user would be authenticated to both the MVC application (getting back a cookie) AND to the REST API (getting back a JWT token).
I tried to achieve this so that instead of using an MVC login page, I would have a React login component, and unauthenticated user would be redirected to that. On submitting the React login form, in the React onSubmit event I would first call TokensController to get JWT token, store that token in browser localStorage, and then submit the form to the MVC controller for the cookie login.
I cannot make MVC redirect to my React login component. Probably some client-side versus server-side routing issue I don't grasp.
In routes.tsx, I am setting my React login component to this route:
<Route path='/login' component={Login} />
In Startup.cs, I have this:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
// Cookie auth for MVC page that hosts React app
.AddCookie(options =>
{
options.LoginPath = new PathString("/Account/Login");
options.LogoutPath = new PathString("/Account/Logout");
options.Cookie = new CookieBuilder
{
HttpOnly = true,
Name = "OurCookieAuthentication",
Path = "/",
SameSite = SameSiteMode.Lax,
SecurePolicy = CookieSecurePolicy.SameAsRequest
};
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.SlidingExpiration = true;
}
)
// JWT authenticaton for REST API called by React app
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("thesecret")),
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5)
};
});
;
services.AddMvc()
}
Above, this currently points to my MVC login page, which works fine for the cookie login but does not handle the JWT:
options.LoginPath = new PathString("/Account/Login");
If I change it to this in an attempt to do the redirect to my React login component, it does not work (my React component is not loaded, probably because request goes to "/login" path on the server MVC controller instead of my client React component):
options.LoginPath = new PathString("/login");
Any ideas on how I could best achieve what I'm after?
have you tried adding the the react view to the MVC AccountController like so:
public IActionResult Login()
{
return View("../Home/Index"); //this should be the cshtml view that serves up the spa
}
Then in your react routes change the login route to:
<Route path='/Account/Login' component={Login} />

Resources