Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

110 fix server side authentification #133

Merged
merged 21 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 47 additions & 74 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,80 +14,6 @@ on:
workflow_dispatch:

jobs:
build-and-deploy-frontend:
name: 'Build and deploy Frontend'
runs-on: ubuntu-latest
environment: Development

defaults:
run:
working-directory: frontend

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Setup Node.js version
uses: actions/setup-node@v3
with:
node-version: "18.x"

- name: Restore cache
uses: actions/cache@v3
with:
path: |
frontend/.next/cache
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
# If source files changed but packages didn't, rebuild from a prior cache.
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}-

- name: Install ci dependencies
run: yarn --frozen-lockfile --ignore-scripts

- name: Build with Next.js (NO)
run: |
yarn build
mv ./build/static ./build/standalone/build
cp -r ./public ./build/standalone/
mv ./build ./build-no
env:
NEXT_PUBLIC_VIBES_BACKEND_URL: ${{ vars.NEXT_PUBLIC_VIBES_BACKEND_URL_NO }}
NEXT_PUBLIC_CLIENT_ID: ${{ vars.NEXT_PUBLIC_CLIENT_ID }}
NEXT_PUBLIC_TENANT_ID: ${{ vars.NEXT_PUBLIC_TENANT_ID }}
NEXT_PUBLIC_APP_SCOPE: ${{ vars.NEXT_PUBLIC_APP_SCOPE }}

- name: Build with Next.js (SE)
run: |
yarn build
mv ./build/static ./build/standalone/build
cp -r ./public ./build/standalone
mv ./build ./build-se
env:
NEXT_PUBLIC_VIBES_BACKEND_URL: ${{ vars.NEXT_PUBLIC_VIBES_BACKEND_URL_SE }}
NEXT_PUBLIC_CLIENT_ID: ${{ vars.NEXT_PUBLIC_CLIENT_ID }}
NEXT_PUBLIC_TENANT_ID: ${{ vars.NEXT_PUBLIC_TENANT_ID }}
NEXT_PUBLIC_APP_SCOPE: ${{ vars.NEXT_PUBLIC_APP_SCOPE }}


- name: Deploy to App Service (NO)
id: deploy-to-webapp-no
uses: azure/webapps-deploy@v2
with:
app-name: 'vibes-frontend-norway-dev'
slot-name: 'Production'
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE_FRONTEND_NO }}
package: ./frontend/build-no/standalone

- name: Deploy to App Service (SE)
id: deploy-to-webapp-se
uses: azure/webapps-deploy@v2
with:
app-name: 'vibes-frontend-sweden-dev'
slot-name: 'Production'
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE_FRONTEND_SE }}
package: ./frontend/build-se/standalone

build-and-deploy-backend:
name: 'Build and deploy Backend'
Expand Down Expand Up @@ -127,3 +53,50 @@ jobs:
app-name: vibes-backend-sweden-dev
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE_BACKEND_SE }} # Define secret variable in repository settings as per action documentation
package: 'backend/Api/build'

build-frontend:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0

- name: Log in to GitHub container registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}

- name: Lowercase the repo name and username
run: echo "REPO=${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV}

- name: Build and push container image to registry
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
with:
push: true
tags: ghcr.io/${{ env.REPO }}-frontend:${{ github.sha }}
file: Frontend.Dockerfile

deploy-frontend:
permissions:
contents: none
runs-on: ubuntu-latest
needs: build-frontend
environment:
name: 'Development'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

steps:
- name: Lowercase the repo name and username
run: echo "REPO=${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV}

- name: Deploy to Azure Web App
id: deploy-to-webapp
uses: azure/webapps-deploy@v2
with:
app-name: vibes-frontend-dev
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE_FRONTEND }}
images: 'ghcr.io/${{ env.REPO }}-frontend:${{ github.sha }}'
28 changes: 3 additions & 25 deletions frontend/Dockerfile → Frontend.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,29 +1,16 @@
FROM node:18-alpine AS base

# Install dependencies only when needed
FROM base AS deps
FROM base AS builder
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app

# Install dependencies based on the preferred package manager
COPY yarn.lock* package.json ./
COPY ./frontend .
RUN yarn --frozen-lockfile --ignore-scripts;


# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /bundle
COPY --from=deps /app/node_modules ./node_modules
COPY . .

RUN yarn build

# If using npm comment out above and use below instead
# RUN npm run build

# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production
Expand All @@ -32,18 +19,9 @@ ENV NODE_ENV production

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /bundle/public ./public

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /bundle/build/standalone ./
COPY --from=builder --chown=nextjs:nodejs /bundle/build/static ./build/static

USER nextjs

EXPOSE 3000
Expand All @@ -52,4 +30,4 @@ ENV PORT 3000
# set hostname to localhost
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]
CMD ["yarn", "start"]
5 changes: 3 additions & 2 deletions backend/Api/Consultants/ConsultantController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public ConsultantController(ApplicationContext context, IMemoryCache cache, Cons
[HttpGet]
public ActionResult<List<ConsultantReadModel>> Get(
[FromQuery(Name = "weeks")] int numberOfWeeks = 8,
[FromQuery(Name = "includeOccupied")] bool includeOccupied = false)
[FromQuery(Name = "includeOccupied")] bool includeOccupied = true)
{
var consultants = GetConsultantsWithAvailability(numberOfWeeks)
.Where(c =>
Expand Down Expand Up @@ -88,7 +88,8 @@ private List<Consultant> LoadConsultantAvailability(int numberOfWeeks)
{
var applicableWeeks = DateService.GetNextWeeks(numberOfWeeks);
var firstDayOfCurrentWeek = DateService.GetFirstDayOfWeekContainingDate(DateTime.Now);
var firstWorkDayOutOfScope = DateService.GetFirstDayOfWeekContainingDate(DateTime.Now.AddDays(numberOfWeeks*7));
var firstWorkDayOutOfScope =
DateService.GetFirstDayOfWeekContainingDate(DateTime.Now.AddDays(numberOfWeeks * 7));

// Needed to filter planned absence and staffing.
// From november, we will span two years.
Expand Down
2 changes: 1 addition & 1 deletion backend/Api/Departments/DeparmentController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Database.DatabaseContext;
using Microsoft.AspNetCore.Mvc;

[Route("/departments")]
[Route("/v0/departments")]
[ApiController]
public class DepartmentController : ControllerBase {

Expand Down
11 changes: 6 additions & 5 deletions frontend/.env.template
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
NEXT_PUBLIC_CLIENT_ID=<client-id> # App Registrations (Azure portal) -> select app reg -> Copy client ID
NEXT_PUBLIC_TENANT_ID=<tenant-id> # App Registrations (Azure portal) -> select app reg -> Copy tenant ID
NEXT_PUBLIC_APP_SCOPE= # App Registrations (Azure portal) -> select app reg -> Expose an API -> Copy scope ID (i.e. api://oidajwoig/...)

# NEXT_PUBLIC_VIBES_BACKEND_URL= #Not necessary for local development, defaults to localhost api
AZURE_AD_CLIENT_ID= # App Registrations (Azure portal) -> select app reg -> Copy client ID
AZURE_AD_TENANT_ID= # App Registrations (Azure portal) -> select app reg -> Copy tenant ID
AZURE_AD_APP_SCOPE= # App Registrations (Azure portal) -> select app reg -> Expose an API -> Copy scope ID (i.e. api://oidajwoig/...)
AZURE_AD_CLIENT_SECRET= #Generate from app registration
NEXTAUTH_SECRET= # High-entroy random secret
NEXTAUTH_URL= # i.e. http://localhost:3000
13 changes: 1 addition & 12 deletions frontend/next.config.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
/** @type {import('next').NextConfig} */

const apiBackendUrl =
process.env.NEXT_PUBLIC_VIBES_BACKEND_URL ?? "http://localhost:7172"; // See backend/launchSettings.json for details on dev-env
// See backend/launchSettings.json for details on dev-env

const nextConfig = {
async rewrites() {
return [
{
source: "/api/:path*",
destination: `${apiBackendUrl}/:path*`,
},
];
},
output: "standalone",
distDir: "build",
swcMinify: true,
modularizeImports: {
Expand Down
5 changes: 2 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
"private": true,
"scripts": {
"dev": "next dev",
"start-test": "NEXT_PUBLIC_NO_AUTH=true next dev",
"start-test": "NEXT_PUBLIC_NO_AUTH=true NEXTAUTH_SECRET='insecure-secret' next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"prettier:ci": "prettier --check . "
},
"dependencies": {
"@azure/msal-browser": "^3.1.0",
"@azure/msal-react": "^2.0.3",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.9",
Expand All @@ -24,6 +22,7 @@
"eslint": "8.48.0",
"eslint-config-next": "^13.5.2",
"next": "^13.5.2",
"next-auth": "^4.23.2",
"postcss": "^8.4.31",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
54 changes: 54 additions & 0 deletions frontend/src/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { AuthOptions, getServerSession, Session } from "next-auth";
import AzureADProvider from "next-auth/providers/azure-ad";

export type CustomSession = {
id_token?: string;
access_token?: string;
} & Session;

export const authOptions: AuthOptions = {
// Configure one or more authentication providers
providers: [
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID!,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET!,
tenantId: process.env.AZURE_AD_TENANT_ID!,
authorization: {
params: {
scope: `openid profile email ${process.env.AZURE_AD_APP_SCOPE}`,
},
},
idToken: true,
}),
],
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 days
},

callbacks: {
async redirect({ baseUrl }) {
return baseUrl;
},
async jwt({ token, account }) {
if (account) {
token.id_token = account.id_token;
token.access_token = account.access_token;
}
return token;
},
async session({ session, token }) {
if (session) {
session = Object.assign({}, session, {
id_token: token.id_token,
access_token: token.access_token,
});
}
return session;
},
},
};

export async function getCustomServerSession(authOptions: AuthOptions) {
return (await getServerSession(authOptions)) as CustomSession;
}
32 changes: 12 additions & 20 deletions frontend/src/app/bemanning/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
"use client";

import FilteredConsultantsList from "@/components/FilteredConsultantsList";
import useVibesApi from "@/hooks/useVibesApi";
import { CircularProgress } from "@mui/material";

export default function Bemanning() {
const { data, isLoading } = useVibesApi(true);

if (isLoading) {
return <CircularProgress />;
}

if (data) {
return (
<div>
<h1>Konsulenter</h1>
<FilteredConsultantsList consultants={data} />
</div>
);
}
import { fetchWithToken } from "@/data/fetchWithToken";
import { Variant } from "@/types";

export default async function Bemanning() {
const consultants = (await fetchWithToken<Variant[]>("variants")) ?? [];

return (
<div>
<h1>Konsulenter</h1>
<FilteredConsultantsList consultants={consultants} />
</div>
);
}
Loading
Loading