Table of contents
Intro
In this post we will see how to do authentication using OpenId for a React App using Orchard Core.
Prerequisites
To create the CMS and and a sample content follow the Getting started
Create a OpenId Client
Navigate to the https://localhost:5001/Admin
and login as a admin.
Navigate to Security -> OpenID Connect -> Scopes -> Add an scope
.
Create two scopes role
and openid
.
Navigate to Security -> OpenID Connect -> Applications -> Add an Application
.
Create a client with
Client id : client1
Display Name : client1
Type : Public client
Flows : Allow Authorization Code Flow
Redirect Uris : http://localhost:3000
Consent type: Explicit consent
Allowed scopes: openid, role
Click Save
Update the CORS policy
Create a new policy to and set is as default. All credentials and any origin, headers and methods.
Update the CORS in CMS Startup.cs
file under ConfigureServices
function.
1public void ConfigureServices(IServiceCollection services) 2{ 3 services.AddCors(o => o.AddDefaultPolicy(builder => 4 { 5 builder 6 .AllowAnyOrigin() 7 .AllowAnyMethod() 8 .AllowAnyHeader(); 9 })); 10 services.AddOrchardCms(); 11}
Create a NextJS App
1yarn create next-app --typescript
Add npm packages
1yarn add oidc-client-ts react-oidc-context @apollo/client
Add .env file
1NEXT_PUBLIC_BASE_URL=https://localhost:5001 2NEXT_PUBLIC_GRAPHQL_URL=https://localhost:5001/api/graphql
Create login component
Inside the components
folder create Login.tsx
1import React from 'react' 2import { useAuth } from 'react-oidc-context'; 3 4const Login = () => { 5 const auth = useAuth(); 6 if (auth.isLoading) { 7 return <div>Loading...</div>; 8 } 9 10 if (auth.error) { 11 return <div>Oops... {auth.error.message}</div>; 12 } 13 14 if (auth.isAuthenticated) { 15 return ( 16 <div> 17 Hello {auth.user?.profile.sub}{" "} 18 <button onClick={auth.removeUser}>Log out</button> 19 </div> 20 ); 21 } 22 23 return <button onClick={auth.signinRedirect}>Log in</button>; 24} 25 26export default Login
Create Languages component
Inside the components
folder create Languages.tsx
1import React from "react"; 2import { gql, useQuery } from "@apollo/client"; 3 4interface Props {} 5 6const LanguagesQuery = gql` 7 query Languages { 8 language { 9 code 10 createdUtc 11 displayText 12 modifiedUtc 13 name 14 } 15 } 16`; 17 18const Languages = (props: Props) => { 19 const { loading, error, data } = useQuery(LanguagesQuery); 20 21 if (loading) return <p>Loading...</p>; 22 if (error) return <p>Error :(</p>; 23 24 return data.language.map((lang: any) => ( 25 <div key={lang.code}> 26 <p>{lang.name}</p> 27 </div> 28 )); 29}; 30 31export default Languages;
Create apollo client
Create a file apollo-client.ts
inside the lib
folder.
1import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client"; 2import { setContext } from "@apollo/client/link/context"; 3import { User } from "oidc-client-ts"; 4 5const httpLink = createHttpLink({ 6 uri: process.env.NEXT_PUBLIC_GRAPHQL_URL, 7}); 8 9const authLink = setContext((_, { headers }) => { 10 // get the authentication token from local storage if it exists 11 const oidcStorage = sessionStorage.getItem( 12 `oidc.user:${process.env.NEXT_PUBLIC_BASE_URL}:client1` 13 ); 14 const token = User.fromStorageString(oidcStorage!).access_token; 15 // return the headers to the context so httpLink can read them 16 return { 17 headers: { 18 ...headers, 19 authorization: token ? `Bearer ${token}` : "", 20 }, 21 }; 22}); 23const client = new ApolloClient({ 24 link: authLink.concat(httpLink), 25 cache: new InMemoryCache(), 26}); 27export default client;
Update _app.tsx
1import '../styles/globals.css' 2import type { AppProps } from 'next/app' 3import { AuthProvider } from "react-oidc-context"; 4import { ApolloProvider } from "@apollo/client"; 5import client from '../lib/apollo-client'; 6function MyApp({ Component, pageProps }: AppProps) { 7 const oidcConfig = { 8 authority: "https://localhost:5001", 9 client_id: "client1", 10 redirect_uri: "http://localhost:3000", 11 response_type: "code", 12 scopes: "openid email" 13 }; 14 const onSignin = () => { 15 location.href = "/"; 16 }; 17 return ( 18 <AuthProvider {...oidcConfig} onSigninCallback={onSignin}> 19 <ApolloProvider client={client}> 20 <Component {...pageProps} /> 21 </ApolloProvider> 22 </AuthProvider> 23 ); 24} 25 26export default MyApp
Update the index page
1import type { NextPage } from 'next' 2import Login from './../components/Login'; 3import Languages from './../components/Languages' 4 5const Home: NextPage = () => { 6 return ( 7 <div> 8 <Login /> 9 <Languages></Languages> 10 </div> 11 ); 12} 13 14export default Home
Run both CMS and Next app
Navigate to the CMS project and run
1dotnet run
Navigate to the NextJS project and run
1yarn // To install the npm packages 2yarn dev // To run the app