diff --git a/api/routes/login.ts b/api/routes/login.ts index f9cbcaecd..5f2b37ce1 100644 --- a/api/routes/login.ts +++ b/api/routes/login.ts @@ -3,6 +3,7 @@ import Err from '@openaddresses/batch-error'; import Auth from '../lib/auth.js'; import Cacher from '../lib/cacher.js'; import busboy from 'busboy'; +import moment from 'moment'; import Config from '../lib/config.js'; import xml2js from 'xml2js'; import { Readable } from 'node:stream'; @@ -16,6 +17,7 @@ import jwt from 'jsonwebtoken'; import { CookieJar, Cookie } from 'tough-cookie'; import { CookieAgent } from 'http-cookie-agent/undici'; import TAKAPI, { APIAuthPassword } from '../lib/tak-api.js'; +import { X509Certificate } from 'crypto'; export default async function router(schema: Schema, config: Config) { await schema.post('/login', { @@ -93,12 +95,13 @@ export default async function router(schema: Schema, config: Config) { if (split.length < 2) throw new Err(500, null, 'Unexpected TAK JWT Format'); const contents: { sub: string; aud: string; nbf: number; exp: number; iat: number; } = JSON.parse(split[1]); + let profile; + const api = await TAKAPI.init(new URL(config.MartiAPI), new APIAuthPassword(req.body.username, req.body.password)); + try { - await config.models.Profile.from(req.body.username); + profile = await config.models.Profile.from(req.body.username); } catch (err) { if (err instanceof Err && err.status === 404) { - const api = await TAKAPI.init(new URL(config.MartiAPI), new APIAuthPassword(req.body.username, req.body.password)); - await config.models.Profile.generate({ username: req.body.username, auth: await api.Credentials.generate() @@ -108,6 +111,23 @@ export default async function router(schema: Schema, config: Config) { } } + let validTo; + + try { + const cert = new X509Certificate(profile.auth.cert); + + validTo = cert.validTo + // The validTo date looks like: 'Mar 6 20:38:58 2025 GMT' + if (moment(validTo, "MMM DD hh:mm:ss YYYY").isBefore(moment().add(7, 'days'))) { + throw new Error('Expired Certificate has expired or is about to'); + } + } catch (err) { + console.error(`Error: CertificateExpiration: ${validTo}: ${err}`); + await config.models.Profile.commit(req.body.username, { + auth: await api.Credentials.generate() + }); + } + return res.json({ access: 'user', diff --git a/api/web/src/components/CloudTAK/Map.vue b/api/web/src/components/CloudTAK/Map.vue index 76d560ee0..014cd978e 100644 --- a/api/web/src/components/CloudTAK/Map.vue +++ b/api/web/src/components/CloudTAK/Map.vue @@ -263,6 +263,7 @@ export default { url.searchParams.append('format', 'geojson'); url.searchParams.append('connection', this.user.email); url.searchParams.append('token', localStorage.token); + if (window.location.hostname === 'localhost') { url.protocol = 'ws:'; } else { @@ -272,7 +273,8 @@ export default { this.ws = new WebSocket(url); this.ws.addEventListener('error', (err) => { this.$emit('err') }); this.ws.addEventListener('close', () => { - this.connectSocket(); + // Otherwise the user is probably logged out + if (localStorage.token) this.connectSocket(); }); this.ws.addEventListener('message', (msg) => { msg = JSON.parse(msg.data);