Skip to content

Commit 4e72a3c

Browse files
committed
added support for authentication
1 parent 4ec7a6f commit 4e72a3c

File tree

12 files changed

+177
-88
lines changed

12 files changed

+177
-88
lines changed

README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ Prisma will connect to the database using the `DATABASE_URL` environment variabl
123123
Create a `.env` file by copying [.env.template](./api/.env.template) inside the [./api](./api) folder and then define the database URL using the following format:
124124
125125
```
126-
DATABASE_URL="sqlserver://DB_SERVER_NAME.database.windows.net:1433;database=DB_NAME;user=DB_USER;password={PASSWORD};encrypt=true;trustServerCertificate=false;loginTimeout=30"
126+
DATABASE_URL="sqlserver://DB_SERVER_NAME.database.windows.net:1433;database=DB_NAME;user=DB_USER;password=DB_PASSWORD;encrypt=true;trustServerCertificate=false;loginTimeout=30"
127127
```
128128
129129
## Create the database schema
@@ -288,3 +288,9 @@ A sample of GraphQL endpoint usage in a web page is available at `/client-graphq
288288
## Azure Static Web App
289289
290290
Azure Static Web App supports a Free tier which is absolutely great so that you can try them for free, but of course don't expect great performances. Initial REST API response will be in the 500 msec area. Keep this in mind if you are planning to use them for something different than testing. If you need better performance right now and cannot when for when Azure Static Web App will be out of preview, you can always deploy the REST API using plain Azure Functions where you can have amazing scalability and performance.
291+
292+
### Authentication
293+
294+
The sample support user authentication via the native Azure Static Web App implementation: [Authentication and authorization for Azure Static Web Apps](https://docs.microsoft.com/en-us/azure/static-web-apps/authentication-authorization)
295+
296+
Each todo item has an associated "ownerId" which is the user who has created that item and only that user can view and operate on that todo. If no user is logged in, only items that belongs to the "anonymous" user will be allowed to be created, managed and accessed.

api/.env.template

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
# Template URL for Azure SQL
33
# DATABASE_URL="sqlserver://DB_SERVER_NAME.database.windows.net:1433;database=DB_NAME;user=DB_USER@DB_SERVER_NAME;password={PASSWORD};encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30"
4+
# SHADOW_DATABASE_URL="sqlserver://DB_SERVER_NAME.database.windows.net:1433;database=DB_NAME;user=DB_USER@DB_SERVER_NAME;password={PASSWORD};encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30"
45

56
# Template URL for SQL Sever
67
DATABASE_URL=sqlserver://localhost:1433;database=prisma-demo;user=SA;password=Prisma1234;trustServerCertificate=true;encrypt=true

api/.vscode/extensions.json

-5
This file was deleted.

api/.vscode/launch.json

-12
This file was deleted.

api/.vscode/settings.json

-8
This file was deleted.

api/.vscode/tasks.json

-23
This file was deleted.

api/graphql/index.ts

+65-17
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,19 @@ const typeDefs = gql`
4343
const resolvers = {
4444
Query: {
4545

46-
todoList: () => {
47-
return prisma.todo.findMany().then(value => value.map(todo => toClientToDo(todo)))
46+
todoList: (parent, args, context) => {
47+
return prisma.todo.findMany({
48+
where: {
49+
ownerId: context.loggedUserId
50+
}
51+
}).then(value => value.map(todo => toClientToDo(todo)))
4852
},
4953

50-
todo: (parent, args) => {
51-
return prisma.todo.findUnique({
54+
todo: (parent, args, context) => {
55+
return prisma.todo.findFirst({
5256
where: {
53-
id: parseInt(args.id, 10)
57+
id: parseInt(args.id, 10),
58+
ownerId: context.loggedUserId
5459
},
5560
}).then(value => toClientToDo(value))
5661
}
@@ -59,33 +64,54 @@ const resolvers = {
5964

6065
Mutation: {
6166

62-
addTodo: (parent, args) => {
67+
addTodo: (parent, args, context) => {
6368
return prisma.todo.create({
6469
data: {
6570
todo: args.title,
66-
completed: args.completed
71+
completed: args.completed,
72+
ownerId: context.loggedUserId
6773
}
6874
}).then(value => toClientToDo(value))
6975
},
7076

71-
updateTodo: (parent, args) => {
72-
return prisma.todo.update({
77+
updateTodo: (parent, args, context) => {
78+
const parsedId = parseInt(args.id, 10)
79+
80+
return prisma.todo.updateMany({
7381
data: {
7482
todo: args.title,
7583
completed: args.completed
7684
},
7785
where: {
78-
id: parseInt(args.id, 10)
86+
id: parsedId,
87+
ownerId: context.loggedUserId
7988
}
89+
}).then(value => {
90+
return prisma.todo.findFirst({
91+
where: {
92+
id: parsedId,
93+
ownerId: context.loggedUserId
94+
}
95+
})
8096
}).then(value => toClientToDo(value))
8197
},
8298

83-
deleteTodo: (parent, args) => {
84-
return prisma.todo.delete({
99+
deleteTodo: (parent, args, context) => {
100+
const parsedId = parseInt(args.id, 10)
101+
102+
return prisma.todo.findFirst({
85103
where: {
86-
id: parseInt(args.id, 10)
104+
id: parseInt(args.id, 10),
105+
ownerId: context.loggedUserId
87106
}
88-
}).then(value => toClientToDo(value))
107+
}).then(value => {
108+
return prisma.todo.deleteMany({
109+
where: {
110+
id: parsedId,
111+
ownerId: context.loggedUserId
112+
}
113+
}).then(_ => toClientToDo(value))
114+
})
89115
}
90116

91117
}
@@ -94,13 +120,35 @@ const resolvers = {
94120
const toClientToDo = (todo: Todo) => {
95121
return {
96122
id: todo.id,
97-
title: todo.todo ,
98-
completed: todo.completed
123+
title: todo.todo,
124+
completed: todo.completed
99125
}
100126
}
101127

128+
const getLoggedUserId = (header: string): string =>
129+
{
130+
if (header)
131+
{
132+
const encoded = Buffer.from(header, 'base64');
133+
const decoded = encoded.toString('ascii');
134+
return JSON.parse(decoded).userId;
135+
}
136+
137+
return "anonymous"
138+
}
139+
102140
// @ts-ignore
103-
const server = new ApolloServer({ typeDefs, resolvers, debug: true, playground: true });
141+
const server = new ApolloServer({
142+
typeDefs,
143+
resolvers,
144+
debug: true,
145+
playground: true,
146+
context: ({ request }) => {
147+
return {
148+
loggedUserId: getLoggedUserId(request.headers['x-ms-client-principal'])
149+
};
150+
},
151+
});
104152

105153
export default server.createHandler({
106154
cors: {

api/prisma/migrations/20210902192319_init/migration.sql renamed to api/prisma/migrations/20210928034429_init/migration.sql

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ BEGIN TRAN;
66
CREATE TABLE [dbo].[Todo] (
77
[id] INT NOT NULL IDENTITY(1,1),
88
[todo] NVARCHAR(100) NOT NULL,
9-
[completed] BIT NOT NULL CONSTRAINT [DF__Todo__completed] DEFAULT 0,
10-
CONSTRAINT [PK__Todo__id] PRIMARY KEY ([id])
9+
[completed] BIT NOT NULL CONSTRAINT [Todo_completed_df] DEFAULT 0,
10+
[ownerId] VARCHAR(128) NOT NULL,
11+
CONSTRAINT [Todo_pkey] PRIMARY KEY ([id])
1112
);
1213

1314
COMMIT TRAN;
@@ -16,7 +17,7 @@ END TRY
1617
BEGIN CATCH
1718

1819
IF @@TRANCOUNT > 0
19-
BEGIN
20+
BEGIN
2021
ROLLBACK TRAN;
2122
END;
2223
THROW

api/prisma/schema.prisma

+8-6
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22
// learn more about it in the docs: https://pris.ly/d/prisma-schema
33

44
datasource db {
5-
provider = "sqlserver"
6-
url = env("DATABASE_URL")
5+
provider = "sqlserver"
6+
url = env("DATABASE_URL")
7+
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
78
}
89

910
generator client {
10-
provider = "prisma-client-js"
11+
provider = "prisma-client-js"
1112
}
1213

1314
model Todo {
14-
id Int @id @default(autoincrement())
15-
todo String @db.NVarChar(100)
16-
completed Boolean @default(false)
15+
id Int @id @default(autoincrement())
16+
todo String @db.NVarChar(100)
17+
completed Boolean @default(false)
18+
ownerId String @db.VarChar(128)
1719
}

0 commit comments

Comments
 (0)