Setting up OIDC authentication in React with IdentityServer4
I’m working on implementing authentication for my React application using IdentityServer4 as the identity provider. I followed the JavaScript client guide from the IdentityServer documentation but I’m having trouble adapting it to work with React.
My current setup
I created an Auth component that renders the main authentication buttons:
import React, { Component } from 'react';
import { signIn, signOut, callService, displayLog } from '../../auth-service';
import { Route } from 'react-router';
export default class AuthComponent extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div>
<button id="signin" onClick={() => { signIn() }}>Sign In</button>
<button id="callservice" onClick={() => { callService() }}>Call Service</button>
<button id="signout" onClick={() => { signOut() }}>Sign Out</button>
<pre id="output"></pre>
</div>
<div>
<Route exact path="/auth-callback" render={() => { window.location.href = "auth-callback.html" }} />
</div>
</div>
);
}
}
And here’s my auth service file:
import Oidc from 'oidc-client';
export function displayLog() {
document.getElementById('output').innerText = '';
Array.prototype.forEach.call(arguments, function (message) {
if (message instanceof Error) {
message = "Error: " + message.message;
}
else if (typeof message !== 'string') {
message = JSON.stringify(message, null, 2);
}
document.getElementById('output').innerHTML += message + '\r\n';
});
}
var settings = {
authority: "http://localhost:5000",
client_id: "react-client",
redirect_uri: "http://localhost:3000/auth-callback.html",
response_type: "id_token token",
scope: "openid profile api1",
post_logout_redirect_uri: "http://localhost:3000/index.html",
};
var userManager = new Oidc.UserManager(settings);
userManager.getUser().then(function (currentUser) {
if (currentUser) {
displayLog("User is authenticated", currentUser.profile);
}
else {
displayLog("User is not authenticated");
}
});
export function signIn() {
userManager.signinRedirect();
}
export function callService() {
userManager.getUser().then(function (currentUser) {
var endpoint = "http://localhost:5001/identity";
var request = new XMLHttpRequest();
request.open("GET", endpoint);
request.onload = function () {
displayLog(request.status, JSON.parse(request.responseText));
}
request.setRequestHeader("Authorization", "Bearer " + currentUser.access_token);
request.send();
});
}
export function signOut() {
userManager.signoutRedirect();
}
The problems I’m facing
When I click sign in, I get redirected to the IdentityServer login page correctly. After entering valid credentials, I’m redirected back to my React app at the callback URL with the token in the fragment. However, the callback handling doesn’t seem to work properly.
The getUser function still shows “User is not authenticated” even after successful login, and the API call button reports no token available. I can see the token is stored in localStorage though.
Also, the logout redirect doesn’t work properly - I get redirected to the IdentityServer logout page but not back to my client after logging out.
Questions
- Is my approach for integrating oidc-client with React correct?
- Should I be using different libraries specifically designed for React?
- How should I properly handle the callback in a React application?
- Are there any good examples of React + IdentityServer4 + oidc-client integration?
Any guidance on what I might be doing wrong would be really helpful. I feel like I’m missing something fundamental about how the callback flow should work in a React SPA.