@fraym/auth 0.3.1 → 0.5.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 +58 -1
- package/dist/cmd/auth.js +17 -3
- package/dist/config/config.d.ts +2 -0
- package/dist/config/config.js +7 -1
- package/dist/management/client.js +7 -7
- package/dist/management/createScope.d.ts +2 -2
- package/dist/management/createScope.js +10 -11
- package/dist/management/deleteScope.d.ts +2 -2
- package/dist/management/deleteScope.js +10 -11
- package/dist/management/getScopes.d.ts +2 -2
- package/dist/management/getScopes.js +11 -10
- package/dist/util/token.d.ts +8 -0
- package/dist/util/token.js +58 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -21,6 +21,11 @@ Use the `auth` cli command to automatically apply your permissions to the auth s
|
|
|
21
21
|
|
|
22
22
|
You can specify the address (and port) of the auth service instance you use in the `AUTH_SERVER_ADDRESS` env variable (default: `127.0.0.1:9000`).
|
|
23
23
|
|
|
24
|
+
In case of scopes api you need to configure the HTTP api:
|
|
25
|
+
|
|
26
|
+
- `AUTH_HTTP_SERVER_ADDRESS`: Http api url of the auth service (default: `http://127.0.0.1`)
|
|
27
|
+
- `AUTH_HTTP_API_TOKEN`: The value of that token has to match the token configured in the auth service
|
|
28
|
+
|
|
24
29
|
The needed schema for auth is a simple enum containing all your permissions. Example:
|
|
25
30
|
|
|
26
31
|
```graphql
|
|
@@ -36,9 +41,55 @@ Use a `.env` file or env variables to configure cte clients and the command:
|
|
|
36
41
|
|
|
37
42
|
```env
|
|
38
43
|
AUTH_SERVER_ADDRESS=127.0.0.1:9000
|
|
44
|
+
AUTH_HTTP_SERVER_ADDRESS=http://127.0.0.1
|
|
45
|
+
AUTH_HTTP_API_TOKEN=
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## JWT functions
|
|
49
|
+
|
|
50
|
+
### Create a new JWT for usage with fraym
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
const jwt = await generateJwt(appSecret, tenantId, scopes, data, expirationTime);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Parameters:
|
|
57
|
+
|
|
58
|
+
- `appSecret`: the secret used to sign the jwt
|
|
59
|
+
- `tenantId`: the id of the tenant to use
|
|
60
|
+
- `scopes`: (optional) list of scopes available in this token
|
|
61
|
+
- `data`: (optional) data added to the `data` field of the token
|
|
62
|
+
- `expirationTime`: (optional) string is resolved to a time span and added to the current timestamp to calculate the expiration time
|
|
63
|
+
|
|
64
|
+
### Add data to an existing JWT
|
|
65
|
+
|
|
66
|
+
Note: this will validate the existing token first.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const jwt = await addDataToJwt(appSecret, token, data);
|
|
39
70
|
```
|
|
40
71
|
|
|
41
|
-
|
|
72
|
+
Parameters:
|
|
73
|
+
|
|
74
|
+
- `appSecret`: the secret used to sign the jwt
|
|
75
|
+
- `token`: the existing jwt
|
|
76
|
+
- `data`: (optional) data added to the `data` field of the token, existing fields in the data object will be overwritten
|
|
77
|
+
|
|
78
|
+
### Validate the token and get associated data
|
|
79
|
+
|
|
80
|
+
Get scopes:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const { scopes, userId, exp } = await getTokenData(appSecret, token, requireUserId);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Parameters:
|
|
87
|
+
|
|
88
|
+
- `appSecret`: the secret used to sign the jwt
|
|
89
|
+
- `token`: the existing jwt
|
|
90
|
+
- `requireUserId`: (optional, default: `true`) If set to true the function will throw an error if it cannot determine the id of the user that owns the jwt
|
|
91
|
+
|
|
92
|
+
## Client Usage
|
|
42
93
|
|
|
43
94
|
### Create the client
|
|
44
95
|
|
|
@@ -56,6 +107,8 @@ The `clientId` paramenter is optional. If none is given the default client will
|
|
|
56
107
|
const scopes = await managementClient.getScopes();
|
|
57
108
|
```
|
|
58
109
|
|
|
110
|
+
Note: you need to configure `AUTH_HTTP_SERVER_ADDRESS` and `AUTH_HTTP_API_TOKEN` to use this function.
|
|
111
|
+
|
|
59
112
|
## Create a scope (permission)
|
|
60
113
|
|
|
61
114
|
The `clientId` paramenter is optional. If none is given the default client will be used.
|
|
@@ -64,6 +117,8 @@ The `clientId` paramenter is optional. If none is given the default client will
|
|
|
64
117
|
await managementClient.createScope("PERMISSION_NAME");
|
|
65
118
|
```
|
|
66
119
|
|
|
120
|
+
Note: you need to configure `AUTH_HTTP_SERVER_ADDRESS` and `AUTH_HTTP_API_TOKEN` to use this function.
|
|
121
|
+
|
|
67
122
|
## Delete a scope (permission)
|
|
68
123
|
|
|
69
124
|
The `clientId` paramenter is optional. If none is given the default client will be used.
|
|
@@ -72,6 +127,8 @@ The `clientId` paramenter is optional. If none is given the default client will
|
|
|
72
127
|
await managementClient.deleteScope("PERMISSION_NAME");
|
|
73
128
|
```
|
|
74
129
|
|
|
130
|
+
Note: you need to configure `AUTH_HTTP_SERVER_ADDRESS` and `AUTH_HTTP_API_TOKEN` to use this function.
|
|
131
|
+
|
|
75
132
|
## Get all roles
|
|
76
133
|
|
|
77
134
|
```typescript
|
package/dist/cmd/auth.js
CHANGED
|
@@ -17,21 +17,31 @@ const run = async () => {
|
|
|
17
17
|
.config({
|
|
18
18
|
schemaGlob: "./src/**/*.graphql",
|
|
19
19
|
serverAddress: "127.0.0.1:9000",
|
|
20
|
+
httpServerAddress: "http://127.0.0.1",
|
|
21
|
+
httpApiToken: "",
|
|
20
22
|
})
|
|
21
23
|
.pkgConf("auth").argv;
|
|
22
24
|
let schemaGlob = argv.schemaGlob;
|
|
23
25
|
let serverAddress = argv.serverAddress;
|
|
26
|
+
let httpServerAddress = argv.httpServerAddress;
|
|
27
|
+
let httpApiToken = argv.httpApiToken;
|
|
24
28
|
if (process.env.AUTH_SCHEMA_GLOB) {
|
|
25
29
|
schemaGlob = process.env.AUTH_SCHEMA_GLOB;
|
|
26
30
|
}
|
|
27
31
|
if (process.env.AUTH_SERVER_ADDRESS) {
|
|
28
32
|
serverAddress = process.env.AUTH_SERVER_ADDRESS;
|
|
29
33
|
}
|
|
34
|
+
if (process.env.AUTH_HTTP_SERVER_ADDRESS) {
|
|
35
|
+
httpServerAddress = process.env.AUTH_HTTP_SERVER_ADDRESS;
|
|
36
|
+
}
|
|
37
|
+
if (process.env.AUTH_HTTP_API_TOKEN) {
|
|
38
|
+
httpApiToken = process.env.AUTH_HTTP_API_TOKEN;
|
|
39
|
+
}
|
|
30
40
|
const schema = await (0, load_1.loadSchema)(`${schemaGlob}`, {
|
|
31
41
|
loaders: [new graphql_file_loader_1.GraphQLFileLoader()],
|
|
32
42
|
});
|
|
33
43
|
const permissions = getSchemaPermissions(schema);
|
|
34
|
-
await migratePermissions(permissions, serverAddress);
|
|
44
|
+
await migratePermissions(permissions, serverAddress, httpServerAddress, httpApiToken);
|
|
35
45
|
};
|
|
36
46
|
const getSchemaPermissions = (schema) => {
|
|
37
47
|
const permissions = [];
|
|
@@ -50,8 +60,12 @@ const getSchemaPermissions = (schema) => {
|
|
|
50
60
|
});
|
|
51
61
|
return permissions;
|
|
52
62
|
};
|
|
53
|
-
const migratePermissions = async (permissions, serverAddress) => {
|
|
54
|
-
const managementClient = await (0, client_1.newManagementClient)({
|
|
63
|
+
const migratePermissions = async (permissions, serverAddress, httpServerAddress, httpApiToken) => {
|
|
64
|
+
const managementClient = await (0, client_1.newManagementClient)({
|
|
65
|
+
serverAddress,
|
|
66
|
+
httpServerAddress,
|
|
67
|
+
httpApiToken,
|
|
68
|
+
});
|
|
55
69
|
const existingPermissions = (await managementClient.getScopes()).filter(permission => !permission.startsWith("FRAYM_"));
|
|
56
70
|
console.log("existingPermissions", existingPermissions);
|
|
57
71
|
const permissionsToCreate = permissions.filter(permission => !existingPermissions.includes(permission));
|
package/dist/config/config.d.ts
CHANGED
package/dist/config/config.js
CHANGED
|
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.useConfigDefaults = exports.getEnvConfig = void 0;
|
|
4
4
|
const dotenv_1 = require("dotenv");
|
|
5
5
|
const getEnvConfig = () => {
|
|
6
|
-
var _a;
|
|
6
|
+
var _a, _b, _c;
|
|
7
7
|
(0, dotenv_1.config)();
|
|
8
8
|
const serverAddress = (_a = process.env.AUTH_SERVER_ADDRESS) !== null && _a !== void 0 ? _a : "";
|
|
9
|
+
const httpServerAddress = (_b = process.env.AUTH_HTTP_SERVER_ADDRESS) !== null && _b !== void 0 ? _b : "";
|
|
10
|
+
const httpApiToken = (_c = process.env.AUTH_HTTP_API_TOKEN) !== null && _c !== void 0 ? _c : "";
|
|
9
11
|
let keepaliveInterval;
|
|
10
12
|
let keepaliveTimeout;
|
|
11
13
|
const keepaliveIntervalString = process.env.AUTH_CONNECTION_KEEPALIVE_INTERVAL;
|
|
@@ -18,6 +20,8 @@ const getEnvConfig = () => {
|
|
|
18
20
|
}
|
|
19
21
|
return {
|
|
20
22
|
serverAddress,
|
|
23
|
+
httpServerAddress,
|
|
24
|
+
httpApiToken,
|
|
21
25
|
keepaliveInterval,
|
|
22
26
|
keepaliveTimeout,
|
|
23
27
|
};
|
|
@@ -30,6 +34,8 @@ const useConfigDefaults = (config) => {
|
|
|
30
34
|
}
|
|
31
35
|
return {
|
|
32
36
|
serverAddress: config.serverAddress,
|
|
37
|
+
httpServerAddress: config.httpServerAddress,
|
|
38
|
+
httpApiToken: config.httpApiToken,
|
|
33
39
|
keepaliveTimeout: (_a = config.keepaliveTimeout) !== null && _a !== void 0 ? _a : 3 * 1000,
|
|
34
40
|
keepaliveInterval: (_b = config.keepaliveInterval) !== null && _b !== void 0 ? _b : 40 * 1000,
|
|
35
41
|
};
|
|
@@ -15,20 +15,20 @@ const getUsers_1 = require("./getUsers");
|
|
|
15
15
|
const updateUser_1 = require("./updateUser");
|
|
16
16
|
const upsertRole_1 = require("./upsertRole");
|
|
17
17
|
const newManagementClient = async (config) => {
|
|
18
|
-
|
|
19
|
-
const serviceClient = new auth_proto_1.ManagementServiceClient(
|
|
20
|
-
"grpc.keepalive_time_ms":
|
|
21
|
-
"grpc.keepalive_timeout_ms":
|
|
18
|
+
const currentConfig = (0, config_1.useConfigDefaults)(config);
|
|
19
|
+
const serviceClient = new auth_proto_1.ManagementServiceClient(currentConfig.serverAddress, grpc_js_1.credentials.createInsecure(), {
|
|
20
|
+
"grpc.keepalive_time_ms": currentConfig.keepaliveInterval,
|
|
21
|
+
"grpc.keepalive_timeout_ms": currentConfig.keepaliveTimeout,
|
|
22
22
|
"grpc.keepalive_permit_without_calls": 1,
|
|
23
23
|
});
|
|
24
24
|
const createScope = async (name, clientId = "") => {
|
|
25
|
-
await (0, createScope_1.createNewScope)(name, clientId,
|
|
25
|
+
await (0, createScope_1.createNewScope)(name, clientId, currentConfig);
|
|
26
26
|
};
|
|
27
27
|
const deleteScope = async (name, clientId = "") => {
|
|
28
|
-
await (0, deleteScope_1.deleteExistingScope)(name, clientId,
|
|
28
|
+
await (0, deleteScope_1.deleteExistingScope)(name, clientId, currentConfig);
|
|
29
29
|
};
|
|
30
30
|
const getScopes = async (clientId = "") => {
|
|
31
|
-
return await (0, getScopes_1.getAllScopes)(clientId,
|
|
31
|
+
return await (0, getScopes_1.getAllScopes)(clientId, currentConfig);
|
|
32
32
|
};
|
|
33
33
|
const upsertRole = async (tenantId, allowedScopes, id = "") => {
|
|
34
34
|
return await (0, upsertRole_1.createOrUpdateRole)(tenantId, id, allowedScopes, serviceClient);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const createNewScope: (name: string, clientId: string,
|
|
1
|
+
import { ClientConfig } from "config/config";
|
|
2
|
+
export declare const createNewScope: (name: string, clientId: string, config: ClientConfig) => Promise<void>;
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createNewScope = void 0;
|
|
4
|
-
const createNewScope = async (name, clientId,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const createNewScope = async (name, clientId, config) => {
|
|
5
|
+
await fetch(`${config.httpServerAddress}/management/scopes`, {
|
|
6
|
+
method: "POST",
|
|
7
|
+
headers: {
|
|
8
|
+
Authorization: `Bearer ${config.httpApiToken}`,
|
|
9
|
+
"Content-Type": "application/json",
|
|
10
|
+
},
|
|
11
|
+
body: JSON.stringify({
|
|
8
12
|
clientId,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
reject(error.message);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
resolve();
|
|
15
|
-
});
|
|
13
|
+
name,
|
|
14
|
+
}),
|
|
16
15
|
});
|
|
17
16
|
};
|
|
18
17
|
exports.createNewScope = createNewScope;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const deleteExistingScope: (name: string, clientId: string,
|
|
1
|
+
import { ClientConfig } from "config/config";
|
|
2
|
+
export declare const deleteExistingScope: (name: string, clientId: string, config: ClientConfig) => Promise<void>;
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.deleteExistingScope = void 0;
|
|
4
|
-
const deleteExistingScope = async (name, clientId,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const deleteExistingScope = async (name, clientId, config) => {
|
|
5
|
+
await fetch(`${config.httpServerAddress}/management/scopes`, {
|
|
6
|
+
method: "DELETE",
|
|
7
|
+
headers: {
|
|
8
|
+
Authorization: `Bearer ${config.httpApiToken}`,
|
|
9
|
+
"Content-Type": "application/json",
|
|
10
|
+
},
|
|
11
|
+
body: JSON.stringify({
|
|
8
12
|
clientId,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
reject(error.message);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
resolve();
|
|
15
|
-
});
|
|
13
|
+
name,
|
|
14
|
+
}),
|
|
16
15
|
});
|
|
17
16
|
};
|
|
18
17
|
exports.deleteExistingScope = deleteExistingScope;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const getAllScopes: (clientId: string,
|
|
1
|
+
import { ClientConfig } from "config/config";
|
|
2
|
+
export declare const getAllScopes: (clientId: string, config: ClientConfig) => Promise<string[]>;
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getAllScopes = void 0;
|
|
4
|
-
const getAllScopes = async (clientId,
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const getAllScopes = async (clientId, config) => {
|
|
5
|
+
const response = await fetch(`${config.httpServerAddress}/management/scopes/list`, {
|
|
6
|
+
method: "POST",
|
|
7
|
+
headers: {
|
|
8
|
+
Authorization: `Bearer ${config.httpApiToken}`,
|
|
9
|
+
"Content-Type": "application/json",
|
|
10
|
+
},
|
|
11
|
+
body: JSON.stringify({
|
|
7
12
|
clientId,
|
|
8
|
-
},
|
|
9
|
-
if (error) {
|
|
10
|
-
reject(error.message);
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
resolve(response.scopes);
|
|
14
|
-
});
|
|
13
|
+
}),
|
|
15
14
|
});
|
|
15
|
+
const data = await response.json();
|
|
16
|
+
return data.scopes;
|
|
16
17
|
};
|
|
17
18
|
exports.getAllScopes = getAllScopes;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const generateJwt: (appSecret: string, tenantId: string, scopes?: string[], data?: Record<string, any>, expirationTime?: string) => Promise<string>;
|
|
2
|
+
export declare const addDataToJwt: (appSecret: string, token: string, data: Record<string, any>) => Promise<string>;
|
|
3
|
+
export interface TokenData {
|
|
4
|
+
userId: string;
|
|
5
|
+
scopes: string[];
|
|
6
|
+
exp: number;
|
|
7
|
+
}
|
|
8
|
+
export declare const getTokenData: (appSecret: string, token: string, requireUserId?: boolean) => Promise<TokenData>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTokenData = exports.addDataToJwt = exports.generateJwt = void 0;
|
|
4
|
+
const jose_1 = require("jose");
|
|
5
|
+
const alg = "HS256";
|
|
6
|
+
const generateJwt = async (appSecret, tenantId, scopes = [], data = {}, expirationTime = "5m") => {
|
|
7
|
+
const secret = new TextEncoder().encode(appSecret);
|
|
8
|
+
return await new jose_1.SignJWT({
|
|
9
|
+
type: "access_token",
|
|
10
|
+
tenantId,
|
|
11
|
+
scopes,
|
|
12
|
+
data,
|
|
13
|
+
})
|
|
14
|
+
.setProtectedHeader({
|
|
15
|
+
alg,
|
|
16
|
+
typ: "JWT",
|
|
17
|
+
})
|
|
18
|
+
.setIssuedAt()
|
|
19
|
+
.setNotBefore("0s")
|
|
20
|
+
.setIssuer("auth")
|
|
21
|
+
.setAudience(["fraym"])
|
|
22
|
+
.setExpirationTime(expirationTime)
|
|
23
|
+
.sign(secret);
|
|
24
|
+
};
|
|
25
|
+
exports.generateJwt = generateJwt;
|
|
26
|
+
const addDataToJwt = async (appSecret, token, data) => {
|
|
27
|
+
var _a;
|
|
28
|
+
const secret = new TextEncoder().encode(appSecret);
|
|
29
|
+
const { payload, protectedHeader } = await (0, jose_1.jwtVerify)(token, secret);
|
|
30
|
+
if (!payload.exp) {
|
|
31
|
+
throw Error("expiration time is missing in JWT");
|
|
32
|
+
}
|
|
33
|
+
const newData = (_a = payload.data) !== null && _a !== void 0 ? _a : {};
|
|
34
|
+
for (let key in data) {
|
|
35
|
+
newData[key] = data[key];
|
|
36
|
+
}
|
|
37
|
+
return new jose_1.SignJWT(Object.assign(Object.assign({}, payload), { data: newData }))
|
|
38
|
+
.setProtectedHeader(protectedHeader)
|
|
39
|
+
.sign(secret);
|
|
40
|
+
};
|
|
41
|
+
exports.addDataToJwt = addDataToJwt;
|
|
42
|
+
const getTokenData = async (appSecret, token, requireUserId = true) => {
|
|
43
|
+
var _a, _b;
|
|
44
|
+
const secret = new TextEncoder().encode(appSecret);
|
|
45
|
+
const { payload } = await (0, jose_1.jwtVerify)(token, secret);
|
|
46
|
+
if (!payload.exp) {
|
|
47
|
+
throw Error("expiration time is missing in JWT");
|
|
48
|
+
}
|
|
49
|
+
if (requireUserId && !payload.jti) {
|
|
50
|
+
throw Error("expiration time is missing in JWT");
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
scopes: (_a = payload.scopes) !== null && _a !== void 0 ? _a : [],
|
|
54
|
+
userId: (_b = payload.jti) !== null && _b !== void 0 ? _b : "",
|
|
55
|
+
exp: payload.exp,
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
exports.getTokenData = getTokenData;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraym/auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"homepage": "https://github.com/fraym/auth-nodejs",
|
|
6
6
|
"repository": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"@grpc/grpc-js": "^1.8.7",
|
|
34
34
|
"dotenv": "^16.0.3",
|
|
35
35
|
"graphql": "^16.6.0",
|
|
36
|
+
"jose": "^4.13.1",
|
|
36
37
|
"yargs": "^17.6.2"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|