@occupop/lib-auth 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +59 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Package @occupop/lib-auth
|
|
2
|
+
|
|
3
|
+
This package is intended to be used with `@occupop/lib-container`
|
|
4
|
+
providing context function for GraphQL (or Express middleware) where AuthUser (or ScopedUser)
|
|
5
|
+
gets validated and signed to container.
|
|
6
|
+
|
|
7
|
+
This implementation refers to
|
|
8
|
+
https://www.apollographql.com/docs/apollo-server/data/context!
|
|
9
|
+
|
|
10
|
+
### *NPM registry token required!*
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
```bash
|
|
14
|
+
# Yarn
|
|
15
|
+
yarn add @occupop/lib-auth
|
|
16
|
+
|
|
17
|
+
# NPM
|
|
18
|
+
npm install @occupop/lib-auth
|
|
19
|
+
|
|
20
|
+
# Bun
|
|
21
|
+
bun add @occupop/lib-auth
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Env Setup
|
|
25
|
+
ENV variables required
|
|
26
|
+
```dotenv
|
|
27
|
+
JWT_SECRET=randomTokenProvidedByAuthService
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Register
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import {
|
|
34
|
+
makeGraphqlServer, graphqlMiddleware, graphqlStandalone
|
|
35
|
+
} from '@occupop/lib-graphql'
|
|
36
|
+
import { resolvers } from './app/resolvers'
|
|
37
|
+
//... all other imports omited...
|
|
38
|
+
|
|
39
|
+
export const container = createTypedContainer({
|
|
40
|
+
container: { asFunction: () => container, singleton: true },
|
|
41
|
+
// ...
|
|
42
|
+
// for graphql:
|
|
43
|
+
authContextValueFunction: { asFunction: makeAuthContextValueFunction, singleton: true },
|
|
44
|
+
// for express:
|
|
45
|
+
authContextMiddleware: { asFunction: makeAuthContextMiddleware, singleton: true },
|
|
46
|
+
// ...
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
**Note:** container need to be self registered/referenced to be possible to context function/middleware register
|
|
51
|
+
AuthUser and ScopedUser, that is extracted from request headers.
|
|
52
|
+
|
|
53
|
+
## Usage (with graphql)
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// server.ts
|
|
57
|
+
import { container } from '.container'
|
|
58
|
+
|
|
59
|
+
const { mongoClient, graphqlServer, graphqlStandalone, authContextValueFunction } = container.cradle
|
|
60
|
+
|
|
61
|
+
await mongoClient.connect()
|
|
62
|
+
console.log('Mongo connected')
|
|
63
|
+
|
|
64
|
+
const httpPort = process.env.API_PORT || 4000
|
|
65
|
+
await await graphqlStandalone(graphqlServer, {
|
|
66
|
+
context: authContextValueFunction,
|
|
67
|
+
listen: { port: httpPort },
|
|
68
|
+
})
|
|
69
|
+
console.log(`Server listening on http://localhost:${httpPort}`)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// app/resolvers.ts
|
|
74
|
+
import { container } from '.container'
|
|
75
|
+
|
|
76
|
+
export const resolvers: GraphQLResolverMap<{ container: typeof container}> = {
|
|
77
|
+
Query: {
|
|
78
|
+
jobs: (source, { skip, take }, { container }, info) => {
|
|
79
|
+
const { getJobsUseCase } = container.cradle
|
|
80
|
+
return getJobsUseCase(skip, take)
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// app/use-cases/get-jobs-use-case.ts
|
|
88
|
+
import { type Deps } from '.container'
|
|
89
|
+
|
|
90
|
+
export function makeGetJobsUseCase({ authUser, jobService }: Deps) {
|
|
91
|
+
return (skip = 0, take = 15) => {
|
|
92
|
+
if (!authUser?.hiringCompanyUuid) throw 'not logged'
|
|
93
|
+
if (take > 100) take = 100
|
|
94
|
+
return jobService.getJobs(authUser.hiringCompanyUuid, skip, take)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
## Usage (with express)
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// server.ts
|
|
106
|
+
import { container } from '.container'
|
|
107
|
+
|
|
108
|
+
const { mongoClient, httpServer, appRouter, authContextMiddleware } = container.cradle
|
|
109
|
+
|
|
110
|
+
await mongoClient.connect()
|
|
111
|
+
console.log('Mongo connected')
|
|
112
|
+
|
|
113
|
+
const httpPort = process.env.API_PORT || 4000
|
|
114
|
+
httpServer.use(authContextMiddleware)
|
|
115
|
+
httpServer.use(appRouter)
|
|
116
|
+
await httpServer.listen(httpPort, () => {
|
|
117
|
+
console.log(`Http listening on http://localhost:${httpPort}`)
|
|
118
|
+
})
|
|
119
|
+
// ...
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// app/router.ts
|
|
124
|
+
export function makeAppRouter({ httpRouter }: Deps) {
|
|
125
|
+
return httpRouter
|
|
126
|
+
//...
|
|
127
|
+
.get('/jobs', (req, res) => {
|
|
128
|
+
const { getJobsUseCase } = req.container.cradle
|
|
129
|
+
res.json(getJobsUseCase(req.query.skip, req.query.take))
|
|
130
|
+
})
|
|
131
|
+
//...
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// app/use-cases/get-jobs-use-case.ts
|
|
137
|
+
import { type Deps } from '.container'
|
|
138
|
+
|
|
139
|
+
export function makeGetJobsUseCase({ authUser, jobService }: Deps) {
|
|
140
|
+
return (skip = 0, take = 15) => {
|
|
141
|
+
if (!authUser?.hiringCompanyUuid) throw 'not logged'
|
|
142
|
+
if (take > 100) take = 100
|
|
143
|
+
return jobService.getJobs(authUser.hiringCompanyUuid, skip, take)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { IncomingMessage, OutgoingMessage } from 'node:http';
|
|
2
|
+
import { type AwilixContainer } from '@occupop/lib-container';
|
|
3
|
+
type Request = IncomingMessage & {
|
|
4
|
+
container?: AwilixContainer;
|
|
5
|
+
};
|
|
6
|
+
type Response = OutgoingMessage;
|
|
7
|
+
export type AuthUser = {
|
|
8
|
+
readonly userUuid: string;
|
|
9
|
+
readonly hiringCompanyUuid?: string;
|
|
10
|
+
readonly recruitmentAgencyUuid?: string;
|
|
11
|
+
readonly sysAdmin?: boolean;
|
|
12
|
+
readonly isAdmin?: boolean;
|
|
13
|
+
readonly isSuperAdmin?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type ScopedUser = {
|
|
16
|
+
readonly userUuid?: undefined;
|
|
17
|
+
readonly [entityUuid: string]: {
|
|
18
|
+
allowedFields: string[];
|
|
19
|
+
} | undefined;
|
|
20
|
+
};
|
|
21
|
+
export declare function getAuthUser(req: Request): AuthUser | null;
|
|
22
|
+
export declare function getScopedUser(req: Request): ScopedUser | null;
|
|
23
|
+
export declare function makeAuthContextValueFunction<IContainer extends AwilixContainer>({ container }: {
|
|
24
|
+
container: IContainer;
|
|
25
|
+
}): (req: Request, res: Response) => {
|
|
26
|
+
container: AwilixContainer<any>;
|
|
27
|
+
};
|
|
28
|
+
export declare function makeAuthContextMiddlewareFunction<IContainer extends AwilixContainer>({ container }: {
|
|
29
|
+
container: IContainer;
|
|
30
|
+
}): (req: Request, res: Response) => void;
|
|
31
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAuthUser = getAuthUser;
|
|
4
|
+
exports.getScopedUser = getScopedUser;
|
|
5
|
+
exports.makeAuthContextValueFunction = makeAuthContextValueFunction;
|
|
6
|
+
exports.makeAuthContextMiddlewareFunction = makeAuthContextMiddlewareFunction;
|
|
7
|
+
const lib_container_1 = require("@occupop/lib-container");
|
|
8
|
+
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
9
|
+
function getToken(req) {
|
|
10
|
+
var _a;
|
|
11
|
+
return (_a = req.headers.authorization) === null || _a === void 0 ? void 0 : _a.replace(/^Bearer /, '');
|
|
12
|
+
}
|
|
13
|
+
function checkToken(token) {
|
|
14
|
+
if (!token)
|
|
15
|
+
return null;
|
|
16
|
+
if (!process.env.JWT_SECRET)
|
|
17
|
+
throw 'Env var JWT_SECRET not set?';
|
|
18
|
+
try {
|
|
19
|
+
return (0, jsonwebtoken_1.verify)(token, process.env.JWT_SECRET);
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function getUserPayload(req) {
|
|
26
|
+
const token = getToken(req);
|
|
27
|
+
return checkToken(token);
|
|
28
|
+
}
|
|
29
|
+
function getAuthUser(req) {
|
|
30
|
+
const user = getUserPayload(req);
|
|
31
|
+
if (user === null || user === void 0 ? void 0 : user.userUuid)
|
|
32
|
+
return user;
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function getScopedUser(req) {
|
|
36
|
+
const user = getUserPayload(req);
|
|
37
|
+
if (user && user.userUuid === undefined)
|
|
38
|
+
return user;
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function makeAuthContextValueFunction({ container }) {
|
|
42
|
+
return (req, res) => {
|
|
43
|
+
const authUser = getAuthUser(req);
|
|
44
|
+
const scopedUser = getScopedUser(req);
|
|
45
|
+
const scopedContainer = container.createScope();
|
|
46
|
+
if (authUser)
|
|
47
|
+
container.register('authUser', (0, lib_container_1.asValue)(authUser));
|
|
48
|
+
if (scopedUser)
|
|
49
|
+
container.register('scopedUser', (0, lib_container_1.asValue)(scopedUser));
|
|
50
|
+
return { container: scopedContainer };
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function makeAuthContextMiddlewareFunction({ container }) {
|
|
54
|
+
return (req, res) => {
|
|
55
|
+
const authContextValueFunction = makeAuthContextValueFunction({ container });
|
|
56
|
+
const contextValue = authContextValueFunction(req, res);
|
|
57
|
+
req.container = contextValue.container;
|
|
58
|
+
};
|
|
59
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@occupop/lib-auth",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"module": "index.ts",
|
|
6
|
+
"description": "...",
|
|
7
|
+
"files": ["dist", "README.md"],
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "bun test",
|
|
13
|
+
"cover": "bun test --coverage",
|
|
14
|
+
"lint": "biome lint",
|
|
15
|
+
"dist": "bun lint && bun cover && rm -fr dist && tsc"
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"types": "dist/index.d.ts",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"jsonwebtoken": "^9.0.2"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@occupop/lib-container": "^1.1.0",
|
|
24
|
+
"typescript": "^5.6.3"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@biomejs/biome": "^1.9.4",
|
|
28
|
+
"@types/bun": "latest",
|
|
29
|
+
"@types/jsonwebtoken": "^9.0.7"
|
|
30
|
+
}
|
|
31
|
+
}
|