Passwordless face login with Entry in the React app
Alex Oliynyk
Posted on May 11, 2022
Entry solves the problem of user Identity. Imagine an app where you do not need to bother about duplicated users, account theft or going into hustle of ensuring that only owner can access an account. That's all possible with Entry and its biometrics authentication engine.
You could read more about it in the docs. And we'll dive straight into the code and build a protected OIDC app.
Or jump to the code, in the repo.
Setup
Let's create a React app, clean it up a bit and install the dependencies:
npx create-react-app entry-demo --template typescript
cd entry-demo
npm install react-oidc-context
rm ./src/App.css ./src/App.test.tsx ./src/logo.svg
mv ./src/App.tsx ./src/app.tsx
To use Entry we need to:
- Register on Entry. I can be done on https://app.xix.ai/.
- Login to Entry and create new
Public
workspace at https://app.xix.ai/workspace/create-new. - Create an
oidc-connect
app at https://app.xix.ai/workspace/alex-test-ws1/admin/apps/new - In the app's config add
http://localhost:3000/*
to the Valid Redirect URIs (comma-separated) field andhttp://localhost:3000
to the Web Origins (comma-separated) field.
OIDC client configuration
We will start creating the OIDC config file:
touch oidc-config.ts
and populate it with
const url = window.location.origin;
export const oidcConfig = {
authority: "https://entry.xix.ai/auth/realms/YOUR_WORKSPACE_NAME",
client_id: "YOUR_APP_ID",
client_secret: 'YOUR_CLIENT_SECRET',
redirect_uri: url,
post_logout_redirect_uri: url,
response_type: "code",
scope: "openid profile email"
};
App's code
Now we need to prepare our index.tsx
file to serve us right:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { AuthProvider } from "react-oidc-context";
import App from './app';
import { oidcConfig } from './oidc-config';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<AuthProvider {...oidcConfig} response_mode="fragment">
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100vh'
}}>
<App />
</div>
</AuthProvider>
</React.StrictMode>
);
And app.tsx
should follow along:
import { useAuth } from "react-oidc-context";
import { LoginPage, ProfilePage } from './pages';
function App() {
const auth = useAuth();
const handleLogoutClick = () => auth.signoutRedirect();
switch (auth.activeNavigator) {
case "signinSilent":
return <div>Signing you in...</div>;
case "signoutRedirect":
return <div>Signing you out...</div>;
}
if (auth.isLoading) {
return <div>Loading...</div>;
}
if (auth.error) {
return <div>
<p>Oops... {auth.error.message}</p>
<button
style={{ padding: '20px' }}
onClick={handleLogoutClick}>Go Back</button>
</div>;
}
return auth.isAuthenticated ? <ProfilePage /> : <LoginPage />
}
export default App;
There are several if ... else
statements here which allow us to properly communicate to the user what is happening during the authentication.
Pages setup
mkdir pages
touch ./pages/login.tsx
touch ./pages/profile.tsx
login.tsx
should look like:
import { useAuth } from "react-oidc-context";
export function LoginPage() {
const auth = useAuth();
return <div>
<button
onClick={() => void auth.signinRedirect({extraQueryParams: { prompt: 'login'}})}
style={{ padding: '20px'}}
>Log in</button>
</div>
}
And profile.tsx
should look like:
import { useAuth } from "react-oidc-context";
export function ProfilePage() {
const auth = useAuth();
const handleLogoutClick = () => {
auth.signoutRedirect();
}
return (
<div style={{ display: 'flex', flexDirection: 'column', maxWidth: '320px'}}>
<h3>Your user data:</h3>
<pre style={{ padding: '20px'}}>
{JSON.stringify(auth.user?.profile, null, 2)}
</pre>
<button style={{ padding: '20px'}} onClick={handleLogoutClick}>Log out</button>
</div>
);
}
To make importing of the pages easier in the pages
folder let's create index.tsx
file and reexport everything from login.tsx
and profile.tsx
.
touch index.tsx
And the content there will be:
export * from './login';
export * from './profile';
What's next
Now that our app is secured we could start building pages and adding new features.
Users who will try to login to your app will be able to register and use it without the need to remember password. Neat, right?
Having troubles with the setup or questions, join our Discord server
Cheers!
Posted on May 11, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.