gammait 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 olillin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/lib/api.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ import oauth2 from 'simple-oauth2';
2
+ import { ClientAuthority, Group, GroupWithPost, Scope, SuperGroup, User, UserId, UserInfo, UserWithGroups } from './types';
3
+ declare abstract class Client {
4
+ protected abstract fetch<T extends object>(url: string, method: string): Promise<T>;
5
+ }
6
+ interface ApiClientConfig {
7
+ authorization: string;
8
+ }
9
+ declare class ApiClient extends Client {
10
+ config: ApiClientConfig;
11
+ constructor(config: ApiClientConfig);
12
+ protected fetch<T extends object>(url: string, method?: string): Promise<T>;
13
+ }
14
+ export declare class ClientApi extends ApiClient {
15
+ getUsers(): Promise<User[]>;
16
+ getUser(id: UserId): Promise<User>;
17
+ getGroups(): Promise<Group[]>;
18
+ getGroupsFor(id: UserId): Promise<GroupWithPost[]>;
19
+ getSuperGroups(): Promise<SuperGroup[]>;
20
+ getAuthorities(): Promise<ClientAuthority[]>;
21
+ getAuthoritiesFor(id: UserId): Promise<ClientAuthority[]>;
22
+ }
23
+ export declare class InfoApi extends ApiClient {
24
+ getUser(id: UserId): Promise<UserWithGroups>;
25
+ }
26
+ export interface AuthorizationCodeConfig {
27
+ clientId: string;
28
+ clientSecret: string;
29
+ redirectUri: string;
30
+ scope: Scope[];
31
+ }
32
+ export declare class AuthorizationCode extends Client {
33
+ config: AuthorizationCodeConfig;
34
+ oauth2Client: oauth2.AuthorizationCode;
35
+ accessToken: oauth2.AccessToken | undefined;
36
+ constructor(config: AuthorizationCodeConfig);
37
+ /**
38
+ * Generate a new access token and store it for later
39
+ * @param code Authorization code
40
+ * @returns The generated access token
41
+ */
42
+ generateToken(code: string): Promise<oauth2.AccessToken>;
43
+ authorizeUrl(): string;
44
+ protected fetch<T extends object>(url: string, method?: string): Promise<T>;
45
+ userInfo(): Promise<UserInfo>;
46
+ }
47
+ export {};
48
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,eAAe,CAAA;AAClC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAG1H,uBAAe,MAAM;IACjB,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CACtF;AAED,UAAU,eAAe;IACrB,aAAa,EAAE,MAAM,CAAA;CACxB;AAED,cAAM,SAAU,SAAQ,MAAM;IAC1B,MAAM,EAAE,eAAe,CAAA;gBAEX,MAAM,EAAE,eAAe;IAKnC,SAAS,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAc,GAAG,OAAO,CAAC,CAAC,CAAC;CA2BrF;AAED,qBAAa,SAAU,SAAQ,SAAS;IACpC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IAI3B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,SAAS,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IAI7B,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAIlD,cAAc,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAIvC,cAAc,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAI5C,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CAG5D;AAED,qBAAa,OAAQ,SAAQ,SAAS;IAClC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAO/C;AAED,MAAM,WAAW,uBAAuB;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,KAAK,EAAE,CAAA;CACjB;AAED,qBAAa,iBAAkB,SAAQ,MAAM;IACzC,MAAM,EAAE,uBAAuB,CAAA;IAC/B,YAAY,EAAE,MAAM,CAAC,iBAAiB,CAAA;IACtC,WAAW,EAAE,MAAM,CAAC,WAAW,GAAG,SAAS,CAAA;gBAE/B,MAAM,EAAE,uBAAuB;IAoB3C;;;;OAIG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC;IAU9D,YAAY;IAOZ,SAAS,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAgClF,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;CAGhC"}
package/lib/api.js ADDED
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.AuthorizationCode = exports.InfoApi = exports.ClientApi = void 0;
40
+ const simple_oauth2_1 = __importDefault(require("simple-oauth2"));
41
+ const url = __importStar(require("./urls"));
42
+ class Client {
43
+ }
44
+ class ApiClient extends Client {
45
+ config;
46
+ constructor(config) {
47
+ super();
48
+ this.config = config;
49
+ }
50
+ fetch(url, method = 'GET') {
51
+ return new Promise((resolve, reject) => {
52
+ fetch(url, {
53
+ headers: {
54
+ Authorization: this.config.authorization,
55
+ },
56
+ method: method,
57
+ })
58
+ .then(async (res) => {
59
+ if (!res.ok) {
60
+ reject(`Received code ${res.status} during ${method} to ${url}`);
61
+ return;
62
+ }
63
+ if (!res.headers.has('Content-Type') || res.headers.get('Content-Type') !== 'application/json') {
64
+ reject('Response was not JSON');
65
+ return;
66
+ }
67
+ const data = await res.json();
68
+ resolve(data);
69
+ })
70
+ .catch(reason => {
71
+ reject(reason);
72
+ });
73
+ });
74
+ }
75
+ }
76
+ class ClientApi extends ApiClient {
77
+ getUsers() {
78
+ return this.fetch(url.CLIENT_API_USERS);
79
+ }
80
+ getUser(id) {
81
+ return this.fetch(url.clientApiUser(id));
82
+ }
83
+ getGroups() {
84
+ return this.fetch(url.CLIENT_API_GROUPS);
85
+ }
86
+ getGroupsFor(id) {
87
+ return this.fetch(url.clientApiGroupsFor(id));
88
+ }
89
+ getSuperGroups() {
90
+ return this.fetch(url.CLIENT_API_SUPER_GROUPS);
91
+ }
92
+ getAuthorities() {
93
+ return this.fetch(url.CLIENT_API_AUTHORITIES);
94
+ }
95
+ getAuthoritiesFor(id) {
96
+ return this.fetch(url.clientApiAuthoritiesFor(id));
97
+ }
98
+ }
99
+ exports.ClientApi = ClientApi;
100
+ class InfoApi extends ApiClient {
101
+ getUser(id) {
102
+ return this.fetch(url.infoApiUser(id));
103
+ }
104
+ }
105
+ exports.InfoApi = InfoApi;
106
+ class AuthorizationCode extends Client {
107
+ config;
108
+ oauth2Client;
109
+ accessToken;
110
+ constructor(config) {
111
+ super();
112
+ this.config = config;
113
+ this.oauth2Client = new simple_oauth2_1.default.AuthorizationCode({
114
+ auth: {
115
+ tokenHost: url.GAMMA_ROOT,
116
+ tokenPath: url.TOKEN_PATH,
117
+ authorizeHost: url.GAMMA_ROOT,
118
+ authorizePath: url.AUTHORIZE_PATH,
119
+ },
120
+ client: {
121
+ id: this.config.clientId,
122
+ secret: this.config.clientSecret,
123
+ },
124
+ options: {
125
+ scopeSeparator: ' ',
126
+ },
127
+ });
128
+ }
129
+ /**
130
+ * Generate a new access token and store it for later
131
+ * @param code Authorization code
132
+ * @returns The generated access token
133
+ */
134
+ async generateToken(code) {
135
+ const token = await this.oauth2Client.getToken({
136
+ code: code,
137
+ scope: this.config.scope,
138
+ redirect_uri: this.config.redirectUri,
139
+ });
140
+ this.accessToken = token;
141
+ return token;
142
+ }
143
+ authorizeUrl() {
144
+ return this.oauth2Client.authorizeURL({
145
+ scope: this.config.scope,
146
+ redirect_uri: this.config.redirectUri,
147
+ });
148
+ }
149
+ fetch(url, method = 'GET') {
150
+ if (!this.accessToken) {
151
+ throw new Error('No token has been generated yet, make sure to run `generateToken`');
152
+ }
153
+ return new Promise((resolve, reject) => {
154
+ fetch(url, {
155
+ headers: {
156
+ Authorization: `Bearer ${this.accessToken.token.access_token}`,
157
+ },
158
+ method: method,
159
+ })
160
+ .then(async (res) => {
161
+ if (!res.ok) {
162
+ reject(`Received code ${res.status} during ${method} to ${url}`);
163
+ return;
164
+ }
165
+ if (!res.headers.has('Content-Type') || res.headers.get('Content-Type') !== 'application/json') {
166
+ reject('Response was not JSON');
167
+ return;
168
+ }
169
+ const data = await res.json();
170
+ resolve(data);
171
+ })
172
+ .catch(reason => {
173
+ reject(reason);
174
+ });
175
+ });
176
+ }
177
+ userInfo() {
178
+ return this.fetch(url.OAUTH2_USERINFO);
179
+ }
180
+ }
181
+ exports.AuthorizationCode = AuthorizationCode;
182
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2FwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxrRUFBa0M7QUFFbEMsNENBQTZCO0FBRTdCLE1BQWUsTUFBTTtDQUVwQjtBQU1ELE1BQU0sU0FBVSxTQUFRLE1BQU07SUFDMUIsTUFBTSxDQUFpQjtJQUV2QixZQUFZLE1BQXVCO1FBQy9CLEtBQUssRUFBRSxDQUFBO1FBQ1AsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUE7SUFDeEIsQ0FBQztJQUVTLEtBQUssQ0FBbUIsR0FBVyxFQUFFLFNBQWlCLEtBQUs7UUFDakUsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtZQUNuQyxLQUFLLENBQUMsR0FBRyxFQUFFO2dCQUNQLE9BQU8sRUFBRTtvQkFDTCxhQUFhLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhO2lCQUMzQztnQkFDRCxNQUFNLEVBQUUsTUFBTTthQUNqQixDQUFDO2lCQUNHLElBQUksQ0FBQyxLQUFLLEVBQUMsR0FBRyxFQUFDLEVBQUU7Z0JBQ2QsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQztvQkFDVixNQUFNLENBQUMsaUJBQWlCLEdBQUcsQ0FBQyxNQUFNLFdBQVcsTUFBTSxPQUFPLEdBQUcsRUFBRSxDQUFDLENBQUE7b0JBQ2hFLE9BQU07Z0JBQ1YsQ0FBQztnQkFFRCxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLElBQUksR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLEtBQUssa0JBQWtCLEVBQUUsQ0FBQztvQkFDN0YsTUFBTSxDQUFDLHVCQUF1QixDQUFDLENBQUE7b0JBQy9CLE9BQU07Z0JBQ1YsQ0FBQztnQkFFRCxNQUFNLElBQUksR0FB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
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './types';
2
+ export * from './api';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,OAAO,CAAA"}
package/lib/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types"), exports);
18
+ __exportStar(require("./api"), exports);
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDBDQUF1QjtBQUN2Qix3Q0FBcUIifQ==
package/lib/types.d.ts ADDED
@@ -0,0 +1,88 @@
1
+ import { UUID } from 'crypto';
2
+ import * as url from './urls';
3
+ export type UserId = UUID;
4
+ export type GroupId = UUID;
5
+ export type SuperGroupId = UUID;
6
+ export type PostId = UUID;
7
+ export interface User {
8
+ id: UserId;
9
+ cid: string;
10
+ nick: string;
11
+ firstName: string;
12
+ lastName: string;
13
+ acceptanceYear: number;
14
+ }
15
+ export interface Group {
16
+ id: GroupId;
17
+ name: string;
18
+ prettyName: string;
19
+ superGroup: SuperGroup;
20
+ }
21
+ export interface SuperGroup {
22
+ id: SuperGroupId;
23
+ name: string;
24
+ prettyName: string;
25
+ type: string;
26
+ svDescription: string;
27
+ enDescription: string;
28
+ }
29
+ export interface Post extends Versioned {
30
+ id: PostId;
31
+ svName: string;
32
+ enName: string;
33
+ }
34
+ export interface PostInfo extends Post {
35
+ emailPrefix: string;
36
+ order: number;
37
+ }
38
+ export interface Versioned {
39
+ version: number;
40
+ }
41
+ export interface VersionedGroup extends Group, Versioned {
42
+ superGroup: VersionedSuperGroup;
43
+ }
44
+ export interface VersionedSuperGroup extends SuperGroup, Versioned {
45
+ }
46
+ export type Scope = 'openid' | 'profile' | 'email';
47
+ export interface ClientConfig {
48
+ clientId: string;
49
+ clientSecret: string;
50
+ scope: Scope;
51
+ }
52
+ export type UserAvatarUrl = `${typeof url.IMAGES_USER_AVATAR}/${UserId}`;
53
+ export type GroupAvatarUrl = `${typeof url.IMAGES_GROUP_AVATAR}/${GroupId}`;
54
+ export type GroupBannerUrl = `${typeof url.IMAGES_GROUP_BANNER}/${GroupId}`;
55
+ export type SuperGroupAvatarUrl = `${typeof url.IMAGES_SUPER_GROUP_AVATAR}/${SuperGroupId}`;
56
+ export type SuperGroupBannerUrl = `${typeof url.IMAGES_SUPER_GROUP_BANNER}/${SuperGroupId}`;
57
+ export type ClientApiUserUrl = `${typeof url.CLIENT_API_USERS}/${UserId}`;
58
+ export type ClientApiGroupsForUrl = `${typeof url.CLIENT_API_GROUPS_FOR}/${UserId}`;
59
+ export type ClientApiAuthoritiesForUrl = `${typeof url.CLIENT_API_AUTHORITIES_FOR}/${UserId}`;
60
+ export type InfoApiUserUrl = `${typeof url.INFO_API_USERS}/${UserId}`;
61
+ export interface UserInfo {
62
+ sub: UserId;
63
+ cid: string;
64
+ name: string;
65
+ given_name: string;
66
+ family_name: string;
67
+ nickname: string;
68
+ picture: UserAvatarUrl;
69
+ scope: Scope[];
70
+ iss: typeof url.GAMMA_ROOT;
71
+ aud: string[];
72
+ nbf: number;
73
+ exp: number;
74
+ iat: number;
75
+ jti: UUID;
76
+ }
77
+ export interface UserWithGroups {
78
+ user: User;
79
+ groups: {
80
+ group: VersionedGroup;
81
+ post: PostInfo;
82
+ }[];
83
+ }
84
+ export interface GroupWithPost extends Group {
85
+ post: Post;
86
+ }
87
+ export type ClientAuthority = string;
88
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/lib/types.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // #endregion API responses
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUEySEEsMkJBQTJCIn0=
package/lib/urls.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ import { ClientApiAuthoritiesForUrl, ClientApiGroupsForUrl, ClientApiUserUrl, GroupAvatarUrl, GroupBannerUrl, GroupId, InfoApiUserUrl, SuperGroupAvatarUrl, SuperGroupBannerUrl, SuperGroupId, UserAvatarUrl, UserId } from './types';
2
+ export declare const GAMMA_ROOT: "https://auth.chalmers.it";
3
+ export declare const OAUTH2_ROOT: "https://auth.chalmers.it/oauth2";
4
+ export declare const AUTHORIZE_PATH: "/oauth2/authorize";
5
+ export declare const AUTHORIZE: "https://auth.chalmers.it/oauth2/authorize";
6
+ export declare const TOKEN_PATH: "/oauth2/token";
7
+ export declare const TOKEN: "https://auth.chalmers.it/oauth2/token";
8
+ export declare const OAUTH2_USERINFO: "https://auth.chalmers.it/oauth2/userinfo";
9
+ export declare const CLIENT_API_ROOT: "https://auth.chalmers.it/api/client/v1";
10
+ export declare const CLIENT_API_GROUPS: "https://auth.chalmers.it/api/client/v1/groups";
11
+ export declare const CLIENT_API_GROUPS_FOR: "https://auth.chalmers.it/api/client/v1/groups/for";
12
+ export declare function clientApiGroupsFor(id: UserId): ClientApiGroupsForUrl;
13
+ export declare const CLIENT_API_SUPER_GROUPS: "https://auth.chalmers.it/api/client/v1/superGroups";
14
+ export declare const CLIENT_API_USERS: "https://auth.chalmers.it/api/client/v1/users";
15
+ export declare function clientApiUser(id: UserId): ClientApiUserUrl;
16
+ export declare const CLIENT_API_AUTHORITIES: "https://auth.chalmers.it/api/client/v1/authorities";
17
+ export declare const CLIENT_API_AUTHORITIES_FOR: "https://auth.chalmers.it/api/client/v1/authorities/for";
18
+ export declare function clientApiAuthoritiesFor(id: UserId): ClientApiAuthoritiesForUrl;
19
+ export declare const INFO_API_ROOT: "https://auth.chalmers.it/api/info/v1";
20
+ export declare const INFO_API_USERS: "https://auth.chalmers.it/api/info/v1/users";
21
+ export declare function infoApiUser(id: UserId): InfoApiUserUrl;
22
+ export declare const INFO_API_BLOB: "https://auth.chalmers.it/api/info/v1/blob";
23
+ export declare const IMAGES: "https://auth.chalmers.it/images";
24
+ export declare const IMAGES_USER_AVATAR: "https://auth.chalmers.it/images/user/avatar";
25
+ export declare function userAvatarUrl(id: UserId): UserAvatarUrl;
26
+ export declare const IMAGES_GROUP_AVATAR: "https://auth.chalmers.it/images/group/avatar";
27
+ export declare function groupAvatarUrl(id: GroupId): GroupAvatarUrl;
28
+ export declare const IMAGES_GROUP_BANNER: "https://auth.chalmers.it/images/group/banner";
29
+ export declare function groupBannerUrl(id: GroupId): GroupBannerUrl;
30
+ export declare const IMAGES_SUPER_GROUP_AVATAR: "https://auth.chalmers.it/images/super-group/avatar";
31
+ export declare function superGroupAvatarUrl(id: SuperGroupId): SuperGroupAvatarUrl;
32
+ export declare const IMAGES_SUPER_GROUP_BANNER: "https://auth.chalmers.it/images/super-group/banner";
33
+ export declare function superGroupBannerUrl(id: SuperGroupId): SuperGroupBannerUrl;
34
+ //# sourceMappingURL=urls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"urls.d.ts","sourceRoot":"","sources":["../src/urls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErO,eAAO,MAAM,UAAU,EAAG,0BAAmC,CAAA;AAG7D,eAAO,MAAM,WAAW,mCAAkC,CAAA;AAC1D,eAAO,MAAM,cAAc,EAAG,mBAA4B,CAAA;AAC1D,eAAO,MAAM,SAAS,6CAA4C,CAAA;AAClE,eAAO,MAAM,UAAU,EAAG,eAAwB,CAAA;AAClD,eAAO,MAAM,KAAK,yCAAwC,CAAA;AAI1D,eAAO,MAAM,eAAe,4CAAqC,CAAA;AAIjE,eAAO,MAAM,eAAe,0CAAyC,CAAA;AACrE,eAAO,MAAM,iBAAiB,iDAAuC,CAAA;AACrE,eAAO,MAAM,qBAAqB,qDAAsC,CAAA;AACxE,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,qBAAqB,CAEpE;AACD,eAAO,MAAM,uBAAuB,sDAA4C,CAAA;AAChF,eAAO,MAAM,gBAAgB,gDAAsC,CAAA;AACnE,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,CAE1D;AACD,eAAO,MAAM,sBAAsB,sDAA4C,CAAA;AAC/E,eAAO,MAAM,0BAA0B,0DAA2C,CAAA;AAClF,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,0BAA0B,CAE9E;AAID,eAAO,MAAM,aAAa,wCAAuC,CAAA;AACjE,eAAO,MAAM,cAAc,8CAAoC,CAAA;AAC/D,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,CAEtD;AACD,eAAO,MAAM,aAAa,6CAAmC,CAAA;AAI7D,eAAO,MAAM,MAAM,mCAAkC,CAAA;AACrD,eAAO,MAAM,kBAAkB,+CAAmC,CAAA;AAClE,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,CAEvD;AACD,eAAO,MAAM,mBAAmB,gDAAoC,CAAA;AACpE,wBAAgB,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,cAAc,CAE1D;AACD,eAAO,MAAM,mBAAmB,gDAAoC,CAAA;AACpE,wBAAgB,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,cAAc,CAE1D;AACD,eAAO,MAAM,yBAAyB,sDAA0C,CAAA;AAChF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,GAAG,mBAAmB,CAEzE;AACD,eAAO,MAAM,yBAAyB,sDAA0C,CAAA;AAChF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,GAAG,mBAAmB,CAEzE"}
package/lib/urls.js ADDED
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IMAGES_SUPER_GROUP_BANNER = exports.IMAGES_SUPER_GROUP_AVATAR = exports.IMAGES_GROUP_BANNER = exports.IMAGES_GROUP_AVATAR = exports.IMAGES_USER_AVATAR = exports.IMAGES = exports.INFO_API_BLOB = exports.INFO_API_USERS = exports.INFO_API_ROOT = exports.CLIENT_API_AUTHORITIES_FOR = exports.CLIENT_API_AUTHORITIES = exports.CLIENT_API_USERS = exports.CLIENT_API_SUPER_GROUPS = exports.CLIENT_API_GROUPS_FOR = exports.CLIENT_API_GROUPS = exports.CLIENT_API_ROOT = exports.OAUTH2_USERINFO = exports.TOKEN = exports.TOKEN_PATH = exports.AUTHORIZE = exports.AUTHORIZE_PATH = exports.OAUTH2_ROOT = exports.GAMMA_ROOT = void 0;
4
+ exports.clientApiGroupsFor = clientApiGroupsFor;
5
+ exports.clientApiUser = clientApiUser;
6
+ exports.clientApiAuthoritiesFor = clientApiAuthoritiesFor;
7
+ exports.infoApiUser = infoApiUser;
8
+ exports.userAvatarUrl = userAvatarUrl;
9
+ exports.groupAvatarUrl = groupAvatarUrl;
10
+ exports.groupBannerUrl = groupBannerUrl;
11
+ exports.superGroupAvatarUrl = superGroupAvatarUrl;
12
+ exports.superGroupBannerUrl = superGroupBannerUrl;
13
+ exports.GAMMA_ROOT = 'https://auth.chalmers.it';
14
+ // #region Authorization
15
+ exports.OAUTH2_ROOT = `${exports.GAMMA_ROOT}/oauth2`;
16
+ exports.AUTHORIZE_PATH = '/oauth2/authorize';
17
+ exports.AUTHORIZE = `${exports.GAMMA_ROOT}${exports.AUTHORIZE_PATH}`;
18
+ exports.TOKEN_PATH = '/oauth2/token';
19
+ exports.TOKEN = `${exports.GAMMA_ROOT}${exports.TOKEN_PATH}`;
20
+ // #endregion Authorization
21
+ // #region OAuth2 Endpoints
22
+ exports.OAUTH2_USERINFO = `${exports.OAUTH2_ROOT}/userinfo`;
23
+ // #endregion
24
+ // #region Client API endpoints
25
+ exports.CLIENT_API_ROOT = `${exports.GAMMA_ROOT}/api/client/v1`;
26
+ exports.CLIENT_API_GROUPS = `${exports.CLIENT_API_ROOT}/groups`;
27
+ exports.CLIENT_API_GROUPS_FOR = `${exports.CLIENT_API_GROUPS}/for`;
28
+ function clientApiGroupsFor(id) {
29
+ return `${exports.CLIENT_API_GROUPS_FOR}/${id}`;
30
+ }
31
+ exports.CLIENT_API_SUPER_GROUPS = `${exports.CLIENT_API_ROOT}/superGroups`;
32
+ exports.CLIENT_API_USERS = `${exports.CLIENT_API_ROOT}/users`;
33
+ function clientApiUser(id) {
34
+ return `${exports.CLIENT_API_USERS}/${id}`;
35
+ }
36
+ exports.CLIENT_API_AUTHORITIES = `${exports.CLIENT_API_ROOT}/authorities`;
37
+ exports.CLIENT_API_AUTHORITIES_FOR = `${exports.CLIENT_API_AUTHORITIES}/for`;
38
+ function clientApiAuthoritiesFor(id) {
39
+ return `${exports.CLIENT_API_AUTHORITIES_FOR}/${id}`;
40
+ }
41
+ // #endregion Client API endpoints
42
+ // #region Info API endpoints
43
+ exports.INFO_API_ROOT = `${exports.GAMMA_ROOT}/api/info/v1`;
44
+ exports.INFO_API_USERS = `${exports.INFO_API_ROOT}/users`;
45
+ function infoApiUser(id) {
46
+ return `${exports.INFO_API_USERS}/${id}`;
47
+ }
48
+ exports.INFO_API_BLOB = `${exports.INFO_API_ROOT}/blob`;
49
+ // #endregion Info API endpoints
50
+ // #region Images
51
+ exports.IMAGES = `${exports.GAMMA_ROOT}/images`;
52
+ exports.IMAGES_USER_AVATAR = `${exports.IMAGES}/user/avatar`;
53
+ function userAvatarUrl(id) {
54
+ return `${exports.IMAGES_USER_AVATAR}/${id}`;
55
+ }
56
+ exports.IMAGES_GROUP_AVATAR = `${exports.IMAGES}/group/avatar`;
57
+ function groupAvatarUrl(id) {
58
+ return `${exports.IMAGES_GROUP_AVATAR}/${id}`;
59
+ }
60
+ exports.IMAGES_GROUP_BANNER = `${exports.IMAGES}/group/banner`;
61
+ function groupBannerUrl(id) {
62
+ return `${exports.IMAGES_GROUP_BANNER}/${id}`;
63
+ }
64
+ exports.IMAGES_SUPER_GROUP_AVATAR = `${exports.IMAGES}/super-group/avatar`;
65
+ function superGroupAvatarUrl(id) {
66
+ return `${exports.IMAGES_SUPER_GROUP_AVATAR}/${id}`;
67
+ }
68
+ exports.IMAGES_SUPER_GROUP_BANNER = `${exports.IMAGES}/super-group/banner`;
69
+ function superGroupBannerUrl(id) {
70
+ return `${exports.IMAGES_SUPER_GROUP_BANNER}/${id}`;
71
+ }
72
+ // #endregion Images
73
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXJscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy91cmxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQW9CQSxnREFFQztBQUdELHNDQUVDO0FBR0QsMERBRUM7QUFNRCxrQ0FFQztBQU9ELHNDQUVDO0FBRUQsd0NBRUM7QUFFRCx3Q0FFQztBQUVELGtEQUVDO0FBRUQsa0RBRUM7QUEvRFksUUFBQSxVQUFVLEdBQUcsMEJBQW1DLENBQUE7QUFFN0Qsd0JBQXdCO0FBQ1gsUUFBQSxXQUFXLEdBQUcsR0FBRyxrQkFBVSxTQUFrQixDQUFBO0FBQzdDLFFBQUEsY0FBYyxHQUFHLG1CQUE0QixDQUFBO0FBQzdDLFFBQUEsU0FBUyxHQUFHLEdBQUcsa0JBQVUsR0FBRyxzQkFBYyxFQUFXLENBQUE7QUFDckQsUUFBQSxVQUFVLEdBQUcsZUFBd0IsQ0FBQTtBQUNyQyxRQUFBLEtBQUssR0FBRyxHQUFHLGtCQUFVLEdBQUcsa0JBQVUsRUFBVyxDQUFBO0FBQzFELDJCQUEyQjtBQUUzQiwyQkFBMkI7QUFDZCxRQUFBLGVBQWUsR0FBRyxHQUFHLG1CQUFXLFdBQW9CLENBQUE7QUFDakUsYUFBYTtBQUViLCtCQUErQjtBQUNsQixRQUFBLGVBQWUsR0FBRyxHQUFHLGtCQUFVLGdCQUF5QixDQUFBO0FBQ3hELFFBQUEsaUJBQWlCLEdBQUcsR0FBRyx1QkFBZSxTQUFrQixDQUFBO0FBQ3hELFFBQUEscUJBQXFCLEdBQUcsR0FBRyx5QkFBaUIsTUFBZSxDQUFBO0FBQ3hFLFNBQWdCLGtCQUFrQixDQUFDLEVBQVU7SUFDekMsT0FBTyxHQUFHLDZCQUFxQixJQUFJLEVBQUUsRUFBRSxDQUFBO0FBQzNDLENBQUM7QUFDWSxRQUFBLHVCQUF1QixHQUFHLEdBQUcsdUJBQWUsY0FBdUIsQ0FBQTtBQUNuRSxRQUFBLGdCQUFnQixHQUFHLEdBQUcsdUJBQWUsUUFBaUIsQ0FBQTtBQUNuRSxTQUFnQixhQUFhLENBQUMsRUFBVTtJQUNwQyxPQUFPLEdBQUcsd0JBQWdCLElBQUksRUFBRSxFQUFFLENBQUE7QUFDdEMsQ0FBQztBQUNZLFFBQUEsc0JBQXNCLEdBQUcsR0FBRyx1QkFBZSxjQUF1QixDQUFBO0FBQ2xFLFFBQUEsMEJBQTBCLEdBQUcsR0FBRyw4QkFBc0IsTUFBZSxDQUFBO0FBQ2xGLFNBQWdCLHVCQUF1QixDQUFDLEVBQVU7SUFDOUMsT0FBTyxHQUFHLGtDQUEwQixJQUFJLEVBQUUsRUFBRSxDQUFBO0FBQ2hELENBQUM7QUFDRCxrQ0FBa0M7QUFFbEMsNkJBQTZCO0FBQ2hCLFFBQUEsYUFBYSxHQUFHLEdBQUcsa0JBQVUsY0FBdUIsQ0FBQTtBQUNwRCxRQUFBLGNBQWMsR0FBRyxHQUFHLHFCQUFhLFFBQWlCLENBQUE7QUFDL0QsU0FBZ0IsV0FBVyxDQUFDLEVBQVU7SUFDbEMsT0FBTyxHQUFHLHNCQUFjLElBQUksRUFBRSxFQUFFLENBQUE7QUFDcEMsQ0FBQztBQUNZLFFBQUEsYUFBYSxHQUFHLEdBQUcscUJBQWEsT0FBZ0IsQ0FBQTtBQUM3RCxnQ0FBZ0M7QUFFaEMsaUJBQWlCO0FBQ0osUUFBQSxNQUFNLEdBQUcsR0FBRyxrQkFBVSxTQUFrQixDQUFBO0FBQ3hDLFFBQUEsa0JBQWtCLEdBQUcsR0FBRyxjQUFNLGNBQXVCLENBQUE7QUFDbEUsU0FBZ0IsYUFBYSxDQUFDLEVBQVU7SUFDcEMsT0FBTyxHQUFHLDBCQUFrQixJQUFJLEVBQUUsRUFBRSxDQUFBO0FBQ3hDLENBQUM7QUFDWSxRQUFBLG1CQUFtQixHQUFHLEdBQUcsY0FBTSxlQUF3QixDQUFBO0FBQ3BFLFNBQWdCLGNBQWMsQ0FBQyxFQUFXO0lBQ3RDLE9BQU8sR0FBRywyQkFBbUIsSUFBSSxFQUFFLEVBQUUsQ0FBQTtBQUN6QyxDQUFDO0FBQ1ksUUFBQSxtQkFBbUIsR0FBRyxHQUFHLGNBQU0sZUFBd0IsQ0FBQTtBQUNwRSxTQUFnQixjQUFjLENBQUMsRUFBVztJQUN0QyxPQUFPLEdBQUcsMkJBQW1CLElBQUksRUFBRSxFQUFFLENBQUE7QUFDekMsQ0FBQztBQUNZLFFBQUEseUJBQXlCLEdBQUcsR0FBRyxjQUFNLHFCQUE4QixDQUFBO0FBQ2hGLFNBQWdCLG1CQUFtQixDQUFDLEVBQWdCO0lBQ2hELE9BQU8sR0FBRyxpQ0FBeUIsSUFBSSxFQUFFLEVBQUUsQ0FBQTtBQUMvQyxDQUFDO0FBQ1ksUUFBQSx5QkFBeUIsR0FBRyxHQUFHLGNBQU0scUJBQThCLENBQUE7QUFDaEYsU0FBZ0IsbUJBQW1CLENBQUMsRUFBZ0I7SUFDaEQsT0FBTyxHQUFHLGlDQUF5QixJQUFJLEVBQUUsRUFBRSxDQUFBO0FBQy9DLENBQUM7QUFDRCxvQkFBb0IifQ==
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "gammait",
3
+ "version": "1.0.0",
4
+ "description": "Interact with the IT-sections account system",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "files": [
8
+ "/lib",
9
+ "/src"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "import": "./lib/index.js",
14
+ "require": "./lib/index.js",
15
+ "types": "./lib/index.d.ts"
16
+ },
17
+ "./urls": {
18
+ "import": "./lib/urls.js",
19
+ "require": "./lib/urls.js",
20
+ "types": "./lib/urls.d.ts"
21
+ }
22
+ },
23
+ "scripts": {
24
+ "build": "rimraf lib && tsc",
25
+ "test": "echo \"Error: no test specified\" && exit 1"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/olillin/gammait.git"
30
+ },
31
+ "keywords": [
32
+ "oauth2",
33
+ "gamma",
34
+ "chalmers"
35
+ ],
36
+ "author": "Oli <oli@olillin.com>",
37
+ "license": "MIT",
38
+ "bugs": {
39
+ "url": "https://github.com/olillin/gammait/issues"
40
+ },
41
+ "homepage": "https://github.com/olillin/gammait#readme",
42
+ "dependencies": {
43
+ "simple-oauth2": "^5.1.0",
44
+ "typescript": "^5.7.3"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^22.13.1",
48
+ "@types/simple-oauth2": "^5.0.7",
49
+ "rimraf": "^6.0.1"
50
+ }
51
+ }
package/src/api.ts ADDED
@@ -0,0 +1,179 @@
1
+ import oauth2 from 'simple-oauth2'
2
+ import { ClientAuthority, Group, GroupWithPost, Scope, SuperGroup, User, UserId, UserInfo, UserWithGroups } from './types'
3
+ import * as url from './urls'
4
+
5
+ abstract class Client {
6
+ protected abstract fetch<T extends object>(url: string, method: string): Promise<T>
7
+ }
8
+
9
+ interface ApiClientConfig {
10
+ authorization: string
11
+ }
12
+
13
+ class ApiClient extends Client {
14
+ config: ApiClientConfig
15
+
16
+ constructor(config: ApiClientConfig) {
17
+ super()
18
+ this.config = config
19
+ }
20
+
21
+ protected fetch<T extends object>(url: string, method: string = 'GET'): Promise<T> {
22
+ return new Promise((resolve, reject) => {
23
+ fetch(url, {
24
+ headers: {
25
+ Authorization: this.config.authorization,
26
+ },
27
+ method: method,
28
+ })
29
+ .then(async res => {
30
+ if (!res.ok) {
31
+ reject(`Received code ${res.status} during ${method} to ${url}`)
32
+ return
33
+ }
34
+
35
+ if (!res.headers.has('Content-Type') || res.headers.get('Content-Type') !== 'application/json') {
36
+ reject('Response was not JSON')
37
+ return
38
+ }
39
+
40
+ const data: T = await res.json()
41
+ resolve(data)
42
+ })
43
+ .catch(reason => {
44
+ reject(reason)
45
+ })
46
+ })
47
+ }
48
+ }
49
+
50
+ export class ClientApi extends ApiClient {
51
+ getUsers(): Promise<User[]> {
52
+ return this.fetch(url.CLIENT_API_USERS)
53
+ }
54
+
55
+ getUser(id: UserId): Promise<User> {
56
+ return this.fetch(url.clientApiUser(id))
57
+ }
58
+
59
+ getGroups(): Promise<Group[]> {
60
+ return this.fetch(url.CLIENT_API_GROUPS)
61
+ }
62
+
63
+ getGroupsFor(id: UserId): Promise<GroupWithPost[]> {
64
+ return this.fetch(url.clientApiGroupsFor(id))
65
+ }
66
+
67
+ getSuperGroups(): Promise<SuperGroup[]> {
68
+ return this.fetch(url.CLIENT_API_SUPER_GROUPS)
69
+ }
70
+
71
+ getAuthorities(): Promise<ClientAuthority[]> {
72
+ return this.fetch(url.CLIENT_API_AUTHORITIES)
73
+ }
74
+
75
+ getAuthoritiesFor(id: UserId): Promise<ClientAuthority[]> {
76
+ return this.fetch(url.clientApiAuthoritiesFor(id))
77
+ }
78
+ }
79
+
80
+ export class InfoApi extends ApiClient {
81
+ getUser(id: UserId): Promise<UserWithGroups> {
82
+ return this.fetch(url.infoApiUser(id))
83
+ }
84
+
85
+ // getBlob(): Promise<unknown> {
86
+ // return this.fetch(url.INFO_API_BLOB)
87
+ // }
88
+ }
89
+
90
+ export interface AuthorizationCodeConfig {
91
+ clientId: string
92
+ clientSecret: string
93
+ redirectUri: string
94
+ scope: Scope[]
95
+ }
96
+
97
+ export class AuthorizationCode extends Client {
98
+ config: AuthorizationCodeConfig
99
+ oauth2Client: oauth2.AuthorizationCode
100
+ accessToken: oauth2.AccessToken | undefined
101
+
102
+ constructor(config: AuthorizationCodeConfig) {
103
+ super()
104
+ this.config = config
105
+ this.oauth2Client = new oauth2.AuthorizationCode({
106
+ auth: {
107
+ tokenHost: url.GAMMA_ROOT,
108
+ tokenPath: url.TOKEN_PATH,
109
+ authorizeHost: url.GAMMA_ROOT,
110
+ authorizePath: url.AUTHORIZE_PATH,
111
+ },
112
+ client: {
113
+ id: this.config.clientId,
114
+ secret: this.config.clientSecret,
115
+ },
116
+ options: {
117
+ scopeSeparator: ' ',
118
+ },
119
+ })
120
+ }
121
+
122
+ /**
123
+ * Generate a new access token and store it for later
124
+ * @param code Authorization code
125
+ * @returns The generated access token
126
+ */
127
+ async generateToken(code: string): Promise<oauth2.AccessToken> {
128
+ const token = await this.oauth2Client.getToken({
129
+ code: code,
130
+ scope: this.config.scope,
131
+ redirect_uri: this.config.redirectUri,
132
+ })
133
+ this.accessToken = token
134
+ return token
135
+ }
136
+
137
+ authorizeUrl() {
138
+ return this.oauth2Client.authorizeURL({
139
+ scope: this.config.scope,
140
+ redirect_uri: this.config.redirectUri,
141
+ })
142
+ }
143
+
144
+ protected fetch<T extends object>(url: string, method: string = 'GET'): Promise<T> {
145
+ if (!this.accessToken) {
146
+ throw new Error('No token has been generated yet, make sure to run `generateToken`')
147
+ }
148
+
149
+ return new Promise((resolve, reject) => {
150
+ fetch(url, {
151
+ headers: {
152
+ Authorization: `Bearer ${this.accessToken!.token.access_token}`,
153
+ },
154
+ method: method,
155
+ })
156
+ .then(async res => {
157
+ if (!res.ok) {
158
+ reject(`Received code ${res.status} during ${method} to ${url}`)
159
+ return
160
+ }
161
+
162
+ if (!res.headers.has('Content-Type') || res.headers.get('Content-Type') !== 'application/json') {
163
+ reject('Response was not JSON')
164
+ return
165
+ }
166
+
167
+ const data: T = await res.json()
168
+ resolve(data)
169
+ })
170
+ .catch(reason => {
171
+ reject(reason)
172
+ })
173
+ })
174
+ }
175
+
176
+ userInfo(): Promise<UserInfo> {
177
+ return this.fetch(url.OAUTH2_USERINFO)
178
+ }
179
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './types'
2
+ export * from './api'
package/src/types.ts ADDED
@@ -0,0 +1,124 @@
1
+ import { UUID } from 'crypto'
2
+ import * as url from './urls'
3
+
4
+ // #region Basic
5
+ export type UserId = UUID
6
+ export type GroupId = UUID
7
+ export type SuperGroupId = UUID
8
+ export type PostId = UUID
9
+
10
+ export interface User {
11
+ id: UserId
12
+ cid: string
13
+ nick: string
14
+ firstName: string
15
+ lastName: string
16
+ acceptanceYear: number
17
+ }
18
+
19
+ export interface Group {
20
+ id: GroupId
21
+ name: string
22
+ prettyName: string
23
+ superGroup: SuperGroup
24
+ }
25
+
26
+ export interface SuperGroup {
27
+ id: SuperGroupId
28
+ name: string
29
+ prettyName: string
30
+ type: string
31
+ svDescription: string
32
+ enDescription: string
33
+ }
34
+
35
+ export interface Post extends Versioned {
36
+ id: PostId
37
+ svName: string
38
+ enName: string
39
+ }
40
+
41
+ export interface PostInfo extends Post {
42
+ emailPrefix: string
43
+ order: number
44
+ }
45
+
46
+ export interface Versioned {
47
+ version: number
48
+ }
49
+
50
+ export interface VersionedGroup extends Group, Versioned {
51
+ superGroup: VersionedSuperGroup
52
+ }
53
+
54
+ export interface VersionedSuperGroup extends SuperGroup, Versioned {}
55
+
56
+ // #endregion Basic
57
+
58
+ // #region Authentication
59
+ export type Scope = 'openid' | 'profile' | 'email'
60
+
61
+ export interface ClientConfig {
62
+ clientId: string
63
+ clientSecret: string
64
+ scope: Scope
65
+ }
66
+ // #endregion Authentication
67
+
68
+ // #region URLs
69
+
70
+ // #region Images
71
+ export type UserAvatarUrl = `${typeof url.IMAGES_USER_AVATAR}/${UserId}`
72
+ export type GroupAvatarUrl = `${typeof url.IMAGES_GROUP_AVATAR}/${GroupId}`
73
+ export type GroupBannerUrl = `${typeof url.IMAGES_GROUP_BANNER}/${GroupId}`
74
+ export type SuperGroupAvatarUrl = `${typeof url.IMAGES_SUPER_GROUP_AVATAR}/${SuperGroupId}`
75
+ export type SuperGroupBannerUrl = `${typeof url.IMAGES_SUPER_GROUP_BANNER}/${SuperGroupId}`
76
+ // #endregion Images
77
+
78
+ // #region Client API
79
+ export type ClientApiUserUrl = `${typeof url.CLIENT_API_USERS}/${UserId}`
80
+ export type ClientApiGroupsForUrl = `${typeof url.CLIENT_API_GROUPS_FOR}/${UserId}`
81
+ export type ClientApiAuthoritiesForUrl = `${typeof url.CLIENT_API_AUTHORITIES_FOR}/${UserId}`
82
+ // #endregion Client API
83
+
84
+ // #region Info API
85
+ export type InfoApiUserUrl = `${typeof url.INFO_API_USERS}/${UserId}`
86
+ // #endregion Info API
87
+
88
+ // #endregion URLs
89
+
90
+ // #region API responses
91
+ export interface UserInfo {
92
+ sub: UserId
93
+ cid: string
94
+
95
+ name: string
96
+ given_name: string
97
+ family_name: string
98
+ nickname: string
99
+ picture: UserAvatarUrl
100
+
101
+ scope: Scope[]
102
+ iss: typeof url.GAMMA_ROOT
103
+
104
+ aud: string[]
105
+ nbf: number
106
+ exp: number
107
+ iat: number
108
+ jti: UUID
109
+ }
110
+
111
+ export interface UserWithGroups {
112
+ user: User
113
+ groups: {
114
+ group: VersionedGroup
115
+ post: PostInfo
116
+ }[]
117
+ }
118
+
119
+ export interface GroupWithPost extends Group {
120
+ post: Post
121
+ }
122
+
123
+ export type ClientAuthority = string
124
+ // #endregion API responses
package/src/urls.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { ClientApiAuthoritiesForUrl, ClientApiGroupsForUrl, ClientApiUserUrl, GroupAvatarUrl, GroupBannerUrl, GroupId, InfoApiUserUrl, SuperGroupAvatarUrl, SuperGroupBannerUrl, SuperGroupId, UserAvatarUrl, UserId } from './types'
2
+
3
+ export const GAMMA_ROOT = 'https://auth.chalmers.it' as const
4
+
5
+ // #region Authorization
6
+ export const OAUTH2_ROOT = `${GAMMA_ROOT}/oauth2` as const
7
+ export const AUTHORIZE_PATH = '/oauth2/authorize' as const
8
+ export const AUTHORIZE = `${GAMMA_ROOT}${AUTHORIZE_PATH}` as const
9
+ export const TOKEN_PATH = '/oauth2/token' as const
10
+ export const TOKEN = `${GAMMA_ROOT}${TOKEN_PATH}` as const
11
+ // #endregion Authorization
12
+
13
+ // #region OAuth2 Endpoints
14
+ export const OAUTH2_USERINFO = `${OAUTH2_ROOT}/userinfo` as const
15
+ // #endregion
16
+
17
+ // #region Client API endpoints
18
+ export const CLIENT_API_ROOT = `${GAMMA_ROOT}/api/client/v1` as const
19
+ export const CLIENT_API_GROUPS = `${CLIENT_API_ROOT}/groups` as const
20
+ export const CLIENT_API_GROUPS_FOR = `${CLIENT_API_GROUPS}/for` as const
21
+ export function clientApiGroupsFor(id: UserId): ClientApiGroupsForUrl {
22
+ return `${CLIENT_API_GROUPS_FOR}/${id}`
23
+ }
24
+ export const CLIENT_API_SUPER_GROUPS = `${CLIENT_API_ROOT}/superGroups` as const
25
+ export const CLIENT_API_USERS = `${CLIENT_API_ROOT}/users` as const
26
+ export function clientApiUser(id: UserId): ClientApiUserUrl {
27
+ return `${CLIENT_API_USERS}/${id}`
28
+ }
29
+ export const CLIENT_API_AUTHORITIES = `${CLIENT_API_ROOT}/authorities` as const
30
+ export const CLIENT_API_AUTHORITIES_FOR = `${CLIENT_API_AUTHORITIES}/for` as const
31
+ export function clientApiAuthoritiesFor(id: UserId): ClientApiAuthoritiesForUrl {
32
+ return `${CLIENT_API_AUTHORITIES_FOR}/${id}`
33
+ }
34
+ // #endregion Client API endpoints
35
+
36
+ // #region Info API endpoints
37
+ export const INFO_API_ROOT = `${GAMMA_ROOT}/api/info/v1` as const
38
+ export const INFO_API_USERS = `${INFO_API_ROOT}/users` as const
39
+ export function infoApiUser(id: UserId): InfoApiUserUrl {
40
+ return `${INFO_API_USERS}/${id}`
41
+ }
42
+ export const INFO_API_BLOB = `${INFO_API_ROOT}/blob` as const
43
+ // #endregion Info API endpoints
44
+
45
+ // #region Images
46
+ export const IMAGES = `${GAMMA_ROOT}/images` as const
47
+ export const IMAGES_USER_AVATAR = `${IMAGES}/user/avatar` as const
48
+ export function userAvatarUrl(id: UserId): UserAvatarUrl {
49
+ return `${IMAGES_USER_AVATAR}/${id}`
50
+ }
51
+ export const IMAGES_GROUP_AVATAR = `${IMAGES}/group/avatar` as const
52
+ export function groupAvatarUrl(id: GroupId): GroupAvatarUrl {
53
+ return `${IMAGES_GROUP_AVATAR}/${id}`
54
+ }
55
+ export const IMAGES_GROUP_BANNER = `${IMAGES}/group/banner` as const
56
+ export function groupBannerUrl(id: GroupId): GroupBannerUrl {
57
+ return `${IMAGES_GROUP_BANNER}/${id}`
58
+ }
59
+ export const IMAGES_SUPER_GROUP_AVATAR = `${IMAGES}/super-group/avatar` as const
60
+ export function superGroupAvatarUrl(id: SuperGroupId): SuperGroupAvatarUrl {
61
+ return `${IMAGES_SUPER_GROUP_AVATAR}/${id}`
62
+ }
63
+ export const IMAGES_SUPER_GROUP_BANNER = `${IMAGES}/super-group/banner` as const
64
+ export function superGroupBannerUrl(id: SuperGroupId): SuperGroupBannerUrl {
65
+ return `${IMAGES_SUPER_GROUP_BANNER}/${id}`
66
+ }
67
+ // #endregion Images