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

KeystoneJS Azure Static Web Apps Session Example #9323

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
92 changes: 92 additions & 0 deletions examples/custom-session-az-swa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# KeystoneJS [Azure Static Web Apps](https://learn.microsoft.com/en-us/azure/static-web-apps/overview) Example

This project demonstrates how to integrate a KeystoneJS backend with a React frontend, deployed on Azure Static Web Apps. It showcases a custom session implementation using Azure's authentication headers. Additionally, it demonstrates how to use roles for authorization on the backend.

## Features

- React frontend with login/logout functionality, and role-based authorization tests
- KeystoneJS backend with authorization based on roles
- Custom session implementation using `x-ms-client-principal` header

## Prerequisites

- Node.js (v14 or later)
- npm or yarn
- An Azure account with an active subscription
- Azure CLI (optional, for deployment)

## Project Structure

```
custom-session-az-swa/
├── frontend/
│ ├── src/
│ │ └── App.js
│ └── package.json
│ └── staticwebapp.config.json
├── backend/
│ ├── keystone.ts
│ ├── schema.ts
│ ├── session.ts
│ └── package.json
├── staticwebapp.config.json
└── README.md
└── swa-cli.config.json
```

## Setup and Installation
1. Install Azure Static Web Apps CLI https://azure.github.io/static-web-apps-cli/docs/use/install. It's required for local execution and testing.
2. Clone the repository:
```
git clone https://github.com/keystonejs/keystone.git
cd examples/custom-session-az-swa
```

3. Install dependencies:
```
cd frontend && npm install
cd ../backend && npm install
```

4. Start the backend:
```
cd backend
npm run dev
```

5. In a new terminal, start the frontend:
```
swa start
```

7. Visit `http://localhost:4280` to see the application running locally.

## Custom Session Implementation

The custom session implementation in `backend/session.ts` uses the `x-ms-client-principal` header provided by Azure Static Web Apps. This header contains encoded user information when a user is authenticated.

## Roles and Authorization
**NOTE: This is a simple example to demonstrate how to use roles for authorization. In a production application, you should use a more secure method to manage roles and permissions.**

The following roles are defined in `backend/session.ts`:
- `blogReader`: Can only read posts
- `blogContributor`: Can read, create, update, and delete posts
- `admin`: Can perform all operations

- Roles are used for authorization on the **Post** list.
```
operation: {
query: ({ session}) => isBlogReader({ session }) || isBlogContributor({ session }),
create: isBlogContributor,
update: isBlogContributor,
delete: isBlogContributor,
},
```

## Important Notes

- Ensure all API routes are prefixed with `/api` in your frontend requests.
- Because backend is "hiden" under a reverse proxy under /api sub path, the Admin dashboard is moved to /api/admin,

- The local development environment may not fully replicate the Azure Static Web Apps authentication. Always test in a staging environment before deploying to production.

23 changes: 23 additions & 0 deletions examples/custom-session-az-swa/backend/keystone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { config } from '@keystone-6/core'
import { lists } from './schema'
import type { TypeInfo } from '.keystone/types'
import { AzStaticWebAppAuthSessionStrategy } from './session'

export default config<TypeInfo>({
server:
{
port: process.env.PORT ? parseInt(process.env.PORT, 10) : 3001,
},
db: {
provider: 'sqlite',
url: process.env.DATABASE_URL ?? 'file:./keystone-example.db',

// WARNING: this is only needed for our monorepo examples, dont do this
prismaClientPath: 'node_modules/myprisma',
},
lists,
session: AzStaticWebAppAuthSessionStrategy,
ui: {
basePath: '/api/admin', // move admin ui under api path
}
})
21 changes: 21 additions & 0 deletions examples/custom-session-az-swa/backend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@keystone-6/example-custom-session-az-swa",
"version": null,
"private": true,
"license": "MIT",
"scripts": {
"dev": "keystone dev",
"start": "keystone start",
"build": "keystone build",
"postinstall": "keystone postinstall"
},
"dependencies": {
"@keystone-6/auth": "^8.0.0",
"@keystone-6/core": "^6.3.0",
"@prisma/client": "5.19.0"
},
"devDependencies": {
"prisma": "5.19.0",
"typescript": "^5.5.0"
}
}
7 changes: 7 additions & 0 deletions examples/custom-session-az-swa/backend/sandbox.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"template": "node",
"container": {
"startScript": "keystone dev",
"node": "20"
}
}
Loading