finki-auth 1.9.0 → 2.0.1

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 CHANGED
@@ -1,6 +1,6 @@
1
- # FINKI Auth
1
+ # FINKI Hub / FINKI Auth
2
2
 
3
- Node.js package for managing authentication and cookies for FCSE's services, built using [axios](https://github.com/axios/axios).
3
+ Node.js package for managing authentication and cookies for FCSE's services.
4
4
 
5
5
  ## Features
6
6
 
@@ -1,7 +1,7 @@
1
1
  import { Service } from './lib/Service.js';
2
2
  export declare class CasAuthentication {
3
+ private readonly cookieJar;
3
4
  private readonly password;
4
- private readonly session;
5
5
  private readonly username;
6
6
  constructor({ password, username }: {
7
7
  password: string;
@@ -1,24 +1,17 @@
1
- import axios from 'axios';
2
- import { wrapper } from 'axios-cookiejar-support';
3
- import { JSDOM } from 'jsdom';
1
+ import * as cheerio from 'cheerio';
2
+ import makeFetchCookie from 'fetch-cookie';
4
3
  import { CookieJar } from 'tough-cookie';
5
- import { z } from 'zod';
6
- import { SERVICE_LOGIN_URLS, SERVICE_URLS } from './constants.js';
4
+ import { SERVICE_LOGIN_URLS } from './constants.js';
7
5
  import { Service } from './lib/Service.js';
8
6
  import { getCookieValidity } from './utils.js';
9
7
  export class CasAuthentication {
8
+ cookieJar;
10
9
  password;
11
- session;
12
10
  username;
13
11
  constructor({ password, username }) {
14
12
  this.username = username;
15
13
  this.password = password;
16
- const cookieJar = new CookieJar();
17
- const client = axios.create({
18
- jar: cookieJar,
19
- validateStatus: (status) => status >= 200 && status < 400,
20
- });
21
- this.session = wrapper(client);
14
+ this.cookieJar = new CookieJar();
22
15
  }
23
16
  static getFullLoginUrl = (service) => {
24
17
  if (service === Service.CAS) {
@@ -27,16 +20,23 @@ export class CasAuthentication {
27
20
  return `${SERVICE_LOGIN_URLS[Service.CAS]}?service=${encodeURIComponent(SERVICE_LOGIN_URLS[service])}`;
28
21
  };
29
22
  authenticate = async (service) => {
30
- const fullUrl = CasAuthentication.getFullLoginUrl(service);
31
- const initialRequest = await this.session.get(fullUrl);
32
- const { data } = z.string().safeParse(initialRequest.data);
33
- if (!data) {
34
- throw new Error('Failed to parse initial request data');
23
+ const jar = new CookieJar();
24
+ const fetchWithCookies = makeFetchCookie(fetch, jar);
25
+ const casLoginUrl = CasAuthentication.getFullLoginUrl(service);
26
+ const initialResponse = await fetchWithCookies(casLoginUrl);
27
+ const html = await initialResponse.text();
28
+ const $ = cheerio.load(html);
29
+ const urlSearchParams = this.getFormData($);
30
+ const postResponse = await fetchWithCookies(casLoginUrl, {
31
+ body: urlSearchParams,
32
+ method: 'POST',
33
+ });
34
+ await postResponse.body?.cancel();
35
+ const serviceLoginUrl = SERVICE_LOGIN_URLS[service];
36
+ const serviceCookies = await jar.getCookies(serviceLoginUrl);
37
+ for (const cookie of serviceCookies) {
38
+ await this.cookieJar.setCookie(cookie, serviceLoginUrl);
35
39
  }
36
- const { window } = new JSDOM(data);
37
- const hiddenInputs = window.document.querySelectorAll('input[type="hidden"]');
38
- const urlSearchParams = this.getFormData(hiddenInputs);
39
- await this.session.post(fullUrl, urlSearchParams);
40
40
  };
41
41
  buildCookieHeader = async (service) => {
42
42
  const cookies = await this.getCookie(service);
@@ -44,27 +44,26 @@ export class CasAuthentication {
44
44
  };
45
45
  getCookie = async (service) => {
46
46
  const serviceLoginUrl = SERVICE_LOGIN_URLS[service];
47
- const serviceCookies = await this.session.defaults.jar?.getCookies(serviceLoginUrl);
48
- return serviceCookies ?? [];
47
+ return await this.cookieJar.getCookies(serviceLoginUrl);
49
48
  };
50
49
  isCookieValid = async (service) => {
51
- const url = SERVICE_URLS[service];
50
+ const serviceLoginUrl = SERVICE_LOGIN_URLS[service];
52
51
  const cookies = await this.getCookie(service);
53
52
  const jar = new CookieJar();
54
53
  for (const cookie of cookies) {
55
- await jar.setCookie(cookie, url);
54
+ await jar.setCookie(cookie, serviceLoginUrl);
56
55
  }
57
56
  return await getCookieValidity({ cookieJar: jar, service });
58
57
  };
59
- getFormData = (inputs) => {
58
+ getFormData = ($) => {
60
59
  const urlSearchParams = new URLSearchParams();
61
- for (const input of inputs) {
62
- const name = input.getAttribute('name');
63
- const value = input.getAttribute('value');
60
+ $('input[type="hidden"]').each((_i, input) => {
61
+ const name = $(input).attr('name');
62
+ const value = $(input).attr('value');
64
63
  if (name) {
65
64
  urlSearchParams.append(name, value ?? '');
66
65
  }
67
- }
66
+ });
68
67
  urlSearchParams.append('username', this.username);
69
68
  urlSearchParams.append('password', this.password);
70
69
  urlSearchParams.append('submit', 'LOGIN');
@@ -21,7 +21,7 @@ export declare const SERVICE_SUCCESS_SELECTORS: {
21
21
  readonly consultations: "a#username";
22
22
  readonly courses: "span.usertext.me-1";
23
23
  readonly diplomas: "#logoutForm > ul > li:nth-child(1) > a";
24
- readonly internships: "span.me-2";
24
+ readonly internships: "span.text-white";
25
25
  readonly masters: "li > a > span";
26
26
  readonly old_courses: "span.usertext.mr-1";
27
27
  };
package/dist/constants.js CHANGED
@@ -22,7 +22,7 @@ export const SERVICE_SUCCESS_SELECTORS = {
22
22
  [Service.CONSULTATIONS]: 'a#username',
23
23
  [Service.COURSES]: 'span.usertext.me-1',
24
24
  [Service.DIPLOMAS]: '#logoutForm > ul > li:nth-child(1) > a',
25
- [Service.INTERNSHIPS]: 'span.me-2',
25
+ [Service.INTERNSHIPS]: 'span.text-white',
26
26
  [Service.MASTERS]: 'li > a > span',
27
27
  [Service.OLD_COURSES]: 'span.usertext.mr-1',
28
28
  };
package/dist/utils.js CHANGED
@@ -1,18 +1,17 @@
1
- import axios from 'axios';
2
- import { wrapper } from 'axios-cookiejar-support';
3
- import { JSDOM } from 'jsdom';
1
+ import * as cheerio from 'cheerio';
2
+ import makeFetchCookie from 'fetch-cookie';
4
3
  import { CookieJar } from 'tough-cookie';
5
- import z from 'zod';
6
4
  import { SERVICE_SUCCESS_SELECTORS, SERVICE_URLS } from './constants.js';
7
5
  export const getCookieValidity = async ({ cookieJar, service, }) => {
8
6
  const url = SERVICE_URLS[service];
9
7
  const userElementSelector = SERVICE_SUCCESS_SELECTORS[service];
10
- const client = wrapper(axios.create({ jar: cookieJar }));
11
- const response = await client.get(url);
12
- const html = z.string().parse(response.data);
13
- const { window } = new JSDOM(html);
14
- const userElement = window.document.querySelector(userElementSelector);
15
- switch (userElement?.textContent) {
8
+ const fetchWithCookies = makeFetchCookie(fetch, cookieJar);
9
+ const response = await fetchWithCookies(url);
10
+ const html = await response.text();
11
+ const $ = cheerio.load(html);
12
+ const userElement = $(userElementSelector).first();
13
+ const textContent = userElement.length > 0 ? userElement.text() : undefined;
14
+ switch (textContent) {
16
15
  case undefined:
17
16
  case 'Најава':
18
17
  return false;
package/package.json CHANGED
@@ -13,30 +13,28 @@
13
13
  }
14
14
  },
15
15
  "dependencies": {
16
- "axios": "^1.13.2",
17
- "axios-cookiejar-support": "^6.0.5",
18
- "jsdom": "^27.4.0",
16
+ "cheerio": "^1.2.0",
17
+ "fetch-cookie": "^3.2.0",
19
18
  "tough-cookie": "^6.0.0",
20
- "zod": "^4.3.2"
19
+ "zod": "^4.3.6"
21
20
  },
22
21
  "description": "Authentication for FCSE services",
23
22
  "devDependencies": {
24
- "@commitlint/cli": "^20.2.0",
25
- "@commitlint/config-conventional": "^20.2.0",
26
- "@types/jsdom": "^27.0.0",
23
+ "@commitlint/cli": "^20.4.2",
24
+ "@commitlint/config-conventional": "^20.4.2",
27
25
  "commitizen": "^4.3.1",
28
26
  "cz-conventional-changelog": "^3.3.0",
29
- "dotenv": "^17.2.3",
30
- "eslint": "^9.39.2",
31
- "eslint-config-imperium": "^3.1.0",
27
+ "dotenv": "^17.3.1",
28
+ "eslint": "^9.39.3",
29
+ "eslint-config-imperium": "^3.4.0",
32
30
  "husky": "^9.1.7",
33
- "rimraf": "^6.1.2",
31
+ "rimraf": "^6.1.3",
34
32
  "semantic-release": "^25.0.2",
35
33
  "typescript": "~5.9.3",
36
- "vitest": "^4.0.16"
34
+ "vitest": "^4.0.18"
37
35
  },
38
36
  "engines": {
39
- "node": "^20 || ^22 || ^24"
37
+ "node": "^22 || ^24"
40
38
  },
41
39
  "files": [
42
40
  "dist",
@@ -70,5 +68,5 @@
70
68
  },
71
69
  "type": "module",
72
70
  "types": "dist/index.d.ts",
73
- "version": "1.9.0"
71
+ "version": "2.0.1"
74
72
  }