Anto Subash.

Orchard Core - Getting Started -Part : 2

Authentication using OpenId in React/NextJs App With Orchard Core - Part 2

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

Repo : https://github.com/antosubash/OrchardCoreReactAuth

Buy Me a Coffee at ko-fi.com