I feel I might be close, but I can't get the msal.js library to work with react.
After logging in via Microsoft I get redirected to my callback but with the code in the URL, e.g.
http://localhost:3000/authcallback#code=0.AQsAuJTIrioCF0ambVF28BQibk37J9vZQ05FkNq4OB...etc
The interaction.status key in local storage is stuck on interaction_in_progress
I'm using the redirect flow and I suspect there is something wrong with how I'm registering the required callback.
Here is my wrapper class AuthService.ts
export class AuthService {
msalClient: PublicClientApplication;
loginRequest: RedirectRequest;
account: AccountInfo | null;
constructor(configuration: Configuration, loginScope: string) {
this.msalClient = new PublicClientApplication(configuration);
this.loginRequest = {
scopes: [loginScope, "openid", "profile"],
};
this.account = null;
}
loadAuthModule(): void {
this.msalClient
.handleRedirectPromise()
.then((resp: AuthenticationResult | null) => {
if (resp) {
this.handleResponse(resp);
}
})
.catch(console.error);
}
handleResponse(response: AuthenticationResult) {
if (response !== null) {
store.dispatch(signIn(response.account));
}
}
signIn(): void {
this.msalClient.loginRedirect(this.loginRequest);
}
signOut(): void {
this.msalClient.logout();
}
async getIdentity(): Promise<AuthenticationResult | null> {
// Stuff
}
}
Here is my config wrapper class
export class AuthConfig {
configuration: Configuration;
loginScope: string;
constructor(clientId: string, authority: string, redirectUrl: string, loginScope: string) {
this.loginScope = loginScope;
this.configuration = {
auth: {
clientId: clientId,
authority: authority,
redirectUri: redirectUrl,
navigateToLoginRequestUrl: false,
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: false,
},
system: {
loggerOptions: {
loggerCallback: (level: any, message: any, containsPii: any) => {
if (containsPii) {
return;
}
switch (level) {
case LogLevel.Error:
console.error(message);
return;
case LogLevel.Info:
console.info(message);
return;
case LogLevel.Verbose:
console.debug(message);
return;
case LogLevel.Warning:
console.warn(message);
return;
}
},
},
},
};
}
}
Then in my login page which is the first page on the site...
export let authService = {} as AuthService;
const LoginPage: React.FC<RouteComponentProps> = ({}) => {
useEffect(() => {
const config = new AuthConfig(
(window as any).ENV.CLIENT_ID,
(window as any).ENV.AUTHORITY,
(window as any).ENV.REDIRECT_URL,
(window as any).ENV.LOGIN_SCOPE
);
authService = new AuthService(config.configuration, config.loginScope);
authService.loadAuthModule();
}, [])
const signIn = () => {
authService.signIn();
};
return (
<Button onClick={() => signIn()}>Sign In</Button>
);
};
export default LoginPage;
My config details are correct. I assume I wouldn't get as far as I can without them being correct.
Where am I going wrong here?
Every example I have seen has hard coded values for the config but this doesn't seem very secure, hence attempting to inject them from my config.js file. I'm open to suggestions on how to do this whilst maintaining a globally accessible msal instance.