@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 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
+
@@ -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
+ }