Next.js (Part 4)

Neil HaddleyNovember 11, 2021

Deploying to Azure

Deploying a Next.js site to Azure

BASH
1% npx create-next-app nextjs-blog --use-npm --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter"
2
3% npm run dev
npx create-next-app

npx create-next-app

localhost:3000

localhost:3000

Azure App Service: Deploy to Web App

Azure App Service: Deploy to Web App

Select the folder to deploy

Select the folder to deploy

Create new Web App...

Create new Web App...

haddley-nextjs-blog

haddley-nextjs-blog

Node 14 LTS

Node 14 LTS

Select a pricing tier

Select a pricing tier

Deploying

Deploying

Deployed

Deployed

Articles

Create /articles and /articles/[id] pages

Updated solution

Deploy update

Deploy

Deploy

Deployment completed

Deployment completed

/articles

/articles

/articles/[id]

/articles/[id]

next-auth

BASH
1% npm install next-auth
Deploy update to Azure

Deploy update to Azure

GitHub OAuth Application

GitHub OAuth Application

next-auth URL, Client ID and Client Secret added to Azure App Service

next-auth URL, Client ID and Client Secret added to Azure App Service

User must be signed in to access /articles

User must be signed in to access /articles

User must be signed in to access /articles/1

User must be signed in to access /articles/1

Sign in

Sign in

Sign in with GitHub

Sign in with GitHub

Authorize the Haddley Nextjs Blog app

Authorize the Haddley Nextjs Blog app

Authorize Haddley

Authorize Haddley

Signed in

Signed in

/articles page

/articles page

/articles/1 page

/articles/1 page

Azure Active Directory

The AzureADProvider from "next-auth/providers/azure-ad" can be used connect Nextjs and Azure Active Directory.

environment settings

environment settings

Application (client) ID, Directory (tenant) ID and 1 secret

Application (client) ID, Directory (tenant) ID and 1 secret

Client Secret

Client Secret

Redirect URIs and Implicit flows

Redirect URIs and Implicit flows

/api/auth/signin

/api/auth/signin

pagesapiauth...nextauth.js pagesarticlesindex.js a...

TEXT
1// /pages/api/auth/[...nextauth].js 
2import NextAuth from "next-auth";
3import Providers from "next-auth/providers";
4
5const options = {
6    providers: [
7        Providers.GitHub({
8            clientId: process.env.NEXTAUTH_GITHUB_ID,
9            clientSecret: process.env.NEXTAUTH_GITHUB_SECRET,
10        }),
11    ],
12}
13
14export default (req, res) => NextAuth(req, res, options)
15
16// /pages/articles/index.js 
17import Link from 'next/link'
18import { getSession } from 'next-auth/client'
19
20function index({ session, articles }) {
21
22    if (!session) { return (<div>You need to be logged on</div>) }
23    
24    return (
25        <div>
26            <ul>
27                {articles.map(article => (<li key={article.id}>
28                    <Link href={`/articles/${article.id}`}>
29                        <a>{article.title}</a>
30                    </Link>
31                </li>))}
32            </ul>
33        </div>
34    )
35}
36
37export default index
38
39export const getServerSideProps = async (context) => {
40
41    const session = await getSession(context);
42
43    // If no session exists, display access denied message
44    if (!session) {
45        return {
46            props: {
47                session,
48            }
49        }
50    }
51
52
53    const res = await fetch('https://jsonplaceholder.typicode.com/posts')
54    const articles = await res.json();
55
56    return {
57        props: {
58            session,
59            articles
60        }
61    }
62}
63
64// /pages/articles/[id]/index.js
65import { getSession } from 'next-auth/client'
66
67function article({ session, article }) {
68
69    if (!session) { return (<div>You need to be logged on</div>) }
70
71    return (
72        <div>
73            <h1>{article.title}</h1>
74            This is article {article.id}
75            <p>{article.body}</p>
76        </div>
77    )
78}
79
80export default article
81
82export const getServerSideProps = async (context) => {
83
84    const session = await getSession(context);
85
86    // If no session exists, display access denied message
87    if (!session) {
88        return {
89            props: {
90                session,
91            }
92        }
93    }
94    
95    const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${context.params.id}`)
96    const article = await res.json()
97    return {
98        props: {
99            session,
100            article
101        }
102    }
103}

...nextauth.js updated

TEXT
1import NextAuth from "next-auth";
2import GihubProvider from "next-auth/providers/github"
3import AzureADProvider from "next-auth/providers/azure-ad"
4
5// https://haddleynexttodo.azurewebsites.net/api/auth/signin
6
7// Authorization callback URL: https://haddleynexttodo.azurewebsites.net/api/auth/callback/github
8// Authorization callback URL: https://haddleynexttodo.azurewebsites.net/api/auth/callback/azure-ad
9
10export default NextAuth({
11    providers: [
12        GihubProvider({
13            clientId: process.env.NEXTAUTH_GITHUB_ID, 
14            clientSecret: process.env.NEXTAUTH_GITHUB_SECRET, 
15        }),
16        AzureADProvider({
17            clientId: process.env.AZURE_AD_CLIENT_ID,
18            clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
19            tenantId: process.env.AZURE_AD_TENANT_ID,
20            authorization: { params: { scope: "openid email profile" } }, 
21        })
22    ],
23    secret: process.env.NEXTAUTH_SECRET,
24    debug: true
25})