Skip to content

Commit

Permalink
303 migrate login system to microsoft entra external (#313)
Browse files Browse the repository at this point in the history
- Completed Azure AD B2C integration
- updated dependencies
- Created global error page
- Renamed `lib` folder to `utils`
- Impelemented generic UI error handler
- Created an event-bus
- Updated DB schema to support new user model
- Updated .env settings for Azure AD B2C
- XDataTable now supports custom Create button label
- Updated implementation of users screen
- Removed approles endpoints
- Refactored AppRoles to a enum
  • Loading branch information
mlhaufe authored Aug 9, 2024
1 parent 1f00c46 commit 0b1acb7
Show file tree
Hide file tree
Showing 83 changed files with 11,984 additions and 10,386 deletions.
18 changes: 9 additions & 9 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
NODE_TLS_REJECT_UNAUTHORIZED=0
NODE_ENV=development
NUXT_ORIGIN: https://localhost:3000
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_HOST: db
POSTGRES_PORT: 5432
POSTGRES_DB: cathedral
GH_CLIENT_ID: Ov23lij8q5YhcosEjIzQ
GH_CLIENT_SECRET: 27c98b69344086c13bc15290e1a8b8c86a910d58
AUTH_SECRET: e792b76a-2573-45d4-a39b-3b8db5712f9c
AUTH_ORIGIN: https://localhost:3000
# SLACK_ADMIN_MEMBER_ID: ***
# SLACK_BOT_TOKEN: ***
# SLACK_SIGNING_SECRET: ***
# APPLICATIONINSIGHTS_CONNECTION_STRING: ***
# APPINSIGHTS_INSTRUMENTATIONKEY: ***
NUXT_SESSION_PASSWORD: ###generate a random string###
NUXT_AUTH_CLIENT_ID: ###contact the team for the client id###
NUXT_AUTH_CLIENT_SECRET: ###contact the team for the client secret###
NUXT_AUTH_TENANT_NAME: cathedralfinalhillb2c
NUXT_AUTH_TENANT_ID: ###contact the team for the tenant id###
NUXT_AUTH_AUTHORITY_DOMAIN: cathedralfinalhillb2c.b2clogin.com
NUXT_AUTH_PRIMARY_USER_FLOW: B2C_1_signupsignin2
33 changes: 21 additions & 12 deletions .github/workflows/azure-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ jobs:
resourceGroupName: ${{ secrets.AZURE_RG }}
template: ./azure/bicep/main.bicep
parameters: >
authOrigin=${{ secrets.AUTH_ORIGIN }}
authSecret=${{ secrets.AUTH_SECRET }}
ghClientId=${{ secrets.GH_CLIENT_ID }}
ghClientSecret=${{ secrets.GH_CLIENT_SECRET }}
postgresDb=${{ secrets.POSTGRES_DB }}
Expand Down Expand Up @@ -81,10 +79,15 @@ jobs:
echo "POSTGRES_HOST=${{secrets.POSTGRES_HOST}}" >> .env
echo "POSTGRES_PORT=${{secrets.POSTGRES_PORT}}" >> .env
echo "POSTGRES_DB=${{secrets.POSTGRES_DB}}" >> .env
echo "GH_CLIENT_ID=${{secrets.GH_CLIENT_ID}}" >> .env
echo "GH_CLIENT_SECRET=${{secrets.GH_CLIENT_SECRET}}" >> .env
echo "AUTH_SECRET=${{secrets.AUTH_SECRET}}" >> .env
echo "AUTH_ORIGIN=${{secrets.AUTH_ORIGIN}}" >> .env
echo "NODE_ENV=production" >> .env
echo "NUXT_ORIGIN=${{secrets.NUXT_ORIGIN}}" >> .env
echo "NUXT_SESSION_PASSWORD=${{secrets.NUXT_SESSION_PASSWORD}}" >> .env
echo "NUXT_AUTH_CLIENT_ID=${{secrets.NUXT_AUTH_CLIENT_ID}}" >> .env
echo "NUXT_AUTH_CLIENT_SECRET=${{secrets.NUXT_AUTH_CLIENT_SECRET}}" >> .env
echo "NUXT_AUTH_TENANT_NAME=${{secrets.NUXT_AUTH_TENANT_NAME}}" >> .env
echo "NUXT_AUTH_TENANT_ID=${{secrets.NUXT_AUTH_TENANT_ID}}" >> .env
echo "NUXT_AUTH_AUTHORITY_DOMAIN=${{secrets.NUXT_AUTH_AUTHORITY_DOMAIN}}" >> .env
echo "NUXT_AUTH_PRIMARY_USER_FLOW=${{secrets.NUXT_AUTH_PRIMARY_USER_FLOW}}" >> .env
echo "SLACK_ADMIN_MEMBER_ID=${{secrets.SLACK_ADMIN_MEMBER_ID}}" >> .env
echo "SLACK_BOT_TOKEN=${{secrets.SLACK_BOT_TOKEN}}" >> .env
echo "SLACK_SIGNING_SECRET=${{secrets.SLACK_SIGNING_SECRET}}" >> .env
Expand Down Expand Up @@ -121,8 +124,6 @@ jobs:
template: ./azure/bicep/main.bicep
deploymentMode: 'Incremental'
parameters: >
authOrigin=${{ secrets.AUTH_ORIGIN }}
authSecret=${{ secrets.AUTH_SECRET }}
ghClientId=${{ secrets.GH_CLIENT_ID }}
ghClientSecret=${{ secrets.GH_CLIENT_SECRET }}
postgresDb=${{ secrets.POSTGRES_DB }}
Expand Down Expand Up @@ -189,10 +190,18 @@ jobs:
echo "POSTGRES_HOST=${{secrets.POSTGRES_HOST}}" >> .env
echo "POSTGRES_PORT=${{secrets.POSTGRES_PORT}}" >> .env
echo "POSTGRES_DB=${{secrets.POSTGRES_DB}}" >> .env
echo "GH_CLIENT_ID=${{secrets.GH_CLIENT_ID}}" >> .env
echo "GH_CLIENT_SECRET=${{secrets.GH_CLIENT_SECRET}}" >> .env
echo "AUTH_SECRET=${{secrets.AUTH_SECRET}}" >> .env
echo "AUTH_ORIGIN=${{secrets.AUTH_ORIGIN}}" >> .env
echo "NODE_ENV=production" >> .env
echo "NUXT_ORIGIN=${{secrets.NUXT_ORIGIN}}" >> .env
echo "NUXT_SESSION_PASSWORD=${{secrets.NUXT_SESSION_PASSWORD}}" >> .env
echo "NUXT_AUTH_CLIENT_ID=${{secrets.NUXT_AUTH_CLIENT_ID}}" >> .env
echo "NUXT_AUTH_CLIENT_SECRET=${{secrets.NUXT_AUTH_CLIENT_SECRET}}" >> .env
echo "NUXT_AUTH_TENANT_NAME=${{secrets.NUXT_AUTH_TENANT_NAME}}" >> .env
echo "NUXT_AUTH_TENANT_ID=${{secrets.NUXT_AUTH_TENANT_ID}}" >> .env
echo "NUXT_AUTH_AUTHORITY_DOMAIN=${{secrets.NUXT_AUTH_AUTHORITY_DOMAIN}}" >> .env
echo "NUXT_AUTH_PRIMARY_USER_FLOW=${{secrets.NUXT_AUTH_PRIMARY_USER_FLOW}}" >> .env
echo "SLACK_ADMIN_MEMBER_ID=${{secrets.SLACK_ADMIN_MEMBER_ID}}" >> .env
echo "SLACK_BOT_TOKEN=${{secrets.SLACK_BOT_TOKEN}}" >> .env
echo "SLACK_SIGNING_SECRET=${{secrets.SLACK_SIGNING_SECRET}}" >> .env
- name: Login to Azure
uses: azure/login@v2
with:
Expand Down
3 changes: 1 addition & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ The application is built using the following primary technologies:
- [PrimeVue](https://primevue.org/)
- [PrimeFlex](https://primeflex.org/)
- [Zod](https://zod.dev/)
- [Nuxt Auth](https://auth.sidebase.io/)
- Which may be replaced by a native Nuxt.js feature in the future (Nuxt 5?)
- [@azure/msal-node](https://www.npmjs.com/package/@azure/msal-node)
- [Node.js](http://nodejs.org/)
- [MikroORM](https://mikro-orm.io/)
- [PostgreSQL](https://www.postgresql.org/)
Expand Down
6 changes: 0 additions & 6 deletions azure/bicep/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ param location string = resourceGroup().location
@maxLength(22)
param name string = 'cathedral'

@secure()
param authOrigin string
@secure()
param authSecret string
@secure()
param ghClientId string
@secure()
Expand Down Expand Up @@ -60,8 +56,6 @@ module appService './modules/appService.bicep' = {
appInsightsInstrumentationKey: appInsights.outputs.appInsightsInstrumentationKey
appInsightsConnectionString: appInsights.outputs.appInsightsConnectionString
name: name
authOrigin: authOrigin
authSecret: authSecret
ghClientId: ghClientId
ghClientSecret: ghClientSecret
postgresDb: postgresDb
Expand Down
12 changes: 0 additions & 12 deletions azure/bicep/modules/appService.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ param appInsightsInstrumentationKey string
@secure()
param appInsightsConnectionString string
@secure()
param authOrigin string
@secure()
param authSecret string
@secure()
param ghClientId string
@secure()
param ghClientSecret string
Expand Down Expand Up @@ -90,14 +86,6 @@ resource appService 'Microsoft.Web/sites@2023-12-01' = {
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: appInsightsConnectionString
}
{
name: 'AUTH_ORIGIN'
value: authOrigin
}
{
name: 'AUTH_SECRET'
value: authSecret
}
{
name: 'GH_CLIENT_ID'
value: ghClientId
Expand Down
14 changes: 5 additions & 9 deletions components/TopNavigation.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts" setup>
import deSlugify from '~/lib/deSlugify';
const router = useRouter()
const { data, signIn, signOut } = useAuth(),
router = useRouter()
let getCrumbs = () => {
const route = router.currentRoute.value,
Expand All @@ -25,9 +24,6 @@ router.afterEach(() => { crumbs.value = getCrumbs(); });
const op = ref();
const toggle = (event: Event) => op.value.toggle(event)
const { data, getSession, signOut, signIn } = useAuth(),
session = await getSession();
</script>

<template>
Expand All @@ -48,15 +44,15 @@ const { data, getSession, signOut, signIn } = useAuth(),
</template>
<template #end>
<Button type="button" @click="toggle" link>
<Avatar v-if="session?.user?.image" :image="session?.user?.image ?? undefined" shape="circle" />
<Avatar v-if="data?.user?.image" :image="data?.user?.image ?? undefined" shape="circle" />
<Avatar v-else icon="pi pi-user" shape="circle" />
</Button>
</template>
</Menubar>
<OverlayPanel ref="op">
<p> {{ data?.user?.name }} </p>
<small> {{ data?.user?.email }} </small>
<p> {{ data?.name }} </p>
<small> {{ data?.email }} </small>
<hr>
<Button v-if="!data?.user" type="button" @click="signIn(undefined)" label="Sign In" icon="pi pi-sign-in" />
<Button v-else type="button" @click="signOut()" label="Sign Out" icon="pi pi-sign-out" />
Expand Down
4 changes: 3 additions & 1 deletion components/XDataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const props = defineProps<{
datasource: RowType[] | null,
filters: Record<string, { value: any, matchMode: string }>,
emptyRecord: { id: string, name: string },
btnCreateLabel?: string,
onCreate: (data: RowType) => Promise<void>,
onDelete: (id: string) => Promise<void>,
onUpdate: (data: RowType) => Promise<void>,
Expand Down Expand Up @@ -126,7 +127,8 @@ const onSort = (event: any) => {
<ConfirmDialog></ConfirmDialog>
<Toolbar>
<template #start>
<Button label="Create" severity="info" @click="onCreateEmpty" :disabled="createDisabled" />
<Button :label="props.btnCreateLabel ?? 'Create'" severity="info" @click="onCreateEmpty"
:disabled="createDisabled" />
</template>
</Toolbar>
<DataTable ref="dataTable" :value="props.datasource as unknown as any[]" dataKey="id" filterDisplay="row"
Expand Down
22 changes: 22 additions & 0 deletions error.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!-- Ref: https://nuxt.com/docs/guide/directory-structure/error -->
<script setup lang="ts">
import type { NuxtError } from '#app'
definePageMeta({
auth: { unauthenticatedOnly: true, navigateAuthenticatedTo: '/' }
})
const props = defineProps({
error: Object as () => NuxtError
})
const handleError = () => clearError({ redirect: '/' })
</script>

<template>
<div>
<h2>{{ error?.statusCode }}</h2>
<pre>{{ error?.message }}</pre>
<Button @click="handleError" label="Clear error" />
</div>
</template>
26 changes: 25 additions & 1 deletion layouts/default.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
<script lang="ts" setup>
import { useToast } from "primevue/usetoast";
const { $eventBus } = useNuxtApp(),
toast = useToast();
const showError = (e: unknown) => {
let error = ''
if (e instanceof Error)
error = e.message
else if (typeof e === 'string')
error = e
else
error = 'An error occurred. Check the browser console for more details.'
console.error(error);
toast.add({ severity: 'error', summary: 'Error', detail: error, group: 'br', life: 5000 });
}
$eventBus.$on('page-error', showError);
</script>
<template>
<TopNavigation />
<section id="content" class="surface-ground">
<slot />
<slot @page-error="showError" />
<Toast position="bottom-right" group="br" />
</section>
<footer>
&copy; Final Hill 2024. All rights reserved. |
Expand Down
3 changes: 0 additions & 3 deletions lib/reCamelCaseToSnakeCase.ts

This file was deleted.

1 change: 0 additions & 1 deletion lib/reSnakeCaseToCamelCase.ts

This file was deleted.

82 changes: 32 additions & 50 deletions migrations/.snapshot-cathedral.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,6 @@
],
"name": "public",
"tables": [
{
"columns": {
"name": {
"name": "name",
"type": "varchar(100)",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"length": 100,
"mappedType": "string"
}
},
"name": "app_role",
"schema": "public",
"indexes": [
{
"keyName": "app_role_pkey",
"columnNames": [
"name"
],
"composite": false,
"constraint": true,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {},
"nativeEnums": {}
},
{
"columns": {
"id": {
Expand Down Expand Up @@ -67,6 +36,16 @@
"length": 6,
"mappedType": "datetime"
},
"last_login_date": {
"name": "last_login_date",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 6,
"mappedType": "datetime"
},
"is_system_admin": {
"name": "is_system_admin",
"type": "boolean",
Expand All @@ -75,6 +54,16 @@
"primary": false,
"nullable": false,
"mappedType": "boolean"
},
"email": {
"name": "email",
"type": "char(254)",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"length": 254,
"mappedType": "character"
}
},
"name": "app_user",
Expand Down Expand Up @@ -186,15 +175,20 @@
"nullable": true,
"mappedType": "uuid"
},
"role_name": {
"name": "role_name",
"type": "varchar(100)",
"role": {
"name": "role",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 100,
"mappedType": "string"
"nullable": false,
"default": "'Organization Reader'",
"enumItems": [
"Organization Admin",
"Organization Contributor",
"Organization Reader"
],
"mappedType": "enum"
}
},
"name": "app_user_organization_role",
Expand All @@ -205,7 +199,7 @@
"columnNames": [
"app_user_id",
"organization_id",
"role_name"
"role"
],
"composite": true,
"constraint": true,
Expand Down Expand Up @@ -238,18 +232,6 @@
],
"referencedTableName": "public.organization",
"deleteRule": "cascade"
},
"app_user_organization_role_role_name_foreign": {
"constraintName": "app_user_organization_role_role_name_foreign",
"columnNames": [
"role_name"
],
"localTableName": "public.app_user_organization_role",
"referencedColumnNames": [
"name"
],
"referencedTableName": "public.app_role",
"deleteRule": "cascade"
}
},
"nativeEnums": {}
Expand Down
Loading

0 comments on commit 0b1acb7

Please sign in to comment.