@tmlmobilidade/emails 20260622.1325.20 → 20260623.1548.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.
@@ -1,6 +1,7 @@
1
1
  import nodemailer from 'nodemailer';
2
2
  export declare class EmailProvider {
3
3
  private static _instance;
4
+ private _refreshToken;
4
5
  private _smtpTransporter;
5
6
  /**
6
7
  * Return the instance of the EmailProvider.
@@ -16,5 +17,13 @@ export declare class EmailProvider {
16
17
  * @returns A promise that resolves when the email is sent.
17
18
  */
18
19
  send(sendMailOptions: nodemailer.SendMailOptions): Promise<void>;
20
+ /**
21
+ * Create an SMTP transporter with the current OAuth2 access token.
22
+ */
23
+ private createSmtpTransporter;
24
+ /**
25
+ * Fetch and cache the Microsoft OAuth2 access token.
26
+ */
27
+ private getRefreshToken;
19
28
  }
20
29
  export declare const emailProvider: EmailProvider;
@@ -1,10 +1,10 @@
1
1
  /* * */
2
2
  import { asyncSingletonProxy } from '@tmlmobilidade/utils';
3
3
  import nodemailer from 'nodemailer';
4
- /* * */
5
4
  export class EmailProvider {
6
5
  //
7
6
  static _instance;
7
+ _refreshToken = { expiresAt: undefined, token: undefined };
8
8
  _smtpTransporter;
9
9
  /**
10
10
  * Return the instance of the EmailProvider.
@@ -33,26 +33,13 @@ export class EmailProvider {
33
33
  throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_AUTH_CLIENT_SECRET');
34
34
  if (!process.env.TML_PROVIDER_EMAIL_AUTH_ACCESS_URL)
35
35
  throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_AUTH_ACCESS_URL');
36
- if (!process.env.TML_PROVIDER_EMAIL_AUTH_REFRESH_TOKEN)
37
- throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_AUTH_REFRESH_TOKEN');
38
36
  if (!process.env.TML_PROVIDER_EMAIL_AUTH_USER)
39
37
  throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_AUTH_USER');
40
38
  if (!process.env.TML_PROVIDER_EMAIL_FROM)
41
39
  throw new Error('Missing required environment variable: TML_PROVIDER_EMAIL_FROM');
40
+ const accessToken = await this.getRefreshToken();
42
41
  // Connect to the SMTP server
43
- this._smtpTransporter = nodemailer.createTransport({
44
- auth: {
45
- accessUrl: process.env.TML_PROVIDER_EMAIL_AUTH_ACCESS_URL,
46
- clientId: process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_ID,
47
- clientSecret: process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_SECRET,
48
- refreshToken: process.env.TML_PROVIDER_EMAIL_AUTH_REFRESH_TOKEN,
49
- type: 'OAuth2',
50
- user: process.env.TML_PROVIDER_EMAIL_AUTH_USER,
51
- },
52
- from: process.env.TML_PROVIDER_EMAIL_FROM,
53
- host: process.env.TML_PROVIDER_EMAIL_SERVER_HOST,
54
- port: Number(process.env.TML_PROVIDER_EMAIL_SERVER_PORT),
55
- });
42
+ this._smtpTransporter = this.createSmtpTransporter(accessToken);
56
43
  return this._smtpTransporter;
57
44
  }
58
45
  catch (error) {
@@ -66,6 +53,10 @@ export class EmailProvider {
66
53
  */
67
54
  async send(sendMailOptions) {
68
55
  try {
56
+ const currentAccessToken = this._refreshToken.token;
57
+ const accessToken = await this.getRefreshToken();
58
+ if (accessToken !== currentAccessToken)
59
+ this._smtpTransporter = this.createSmtpTransporter(accessToken);
69
60
  await this._smtpTransporter.sendMail({
70
61
  ...this._smtpTransporter.options,
71
62
  ...sendMailOptions,
@@ -75,5 +66,53 @@ export class EmailProvider {
75
66
  throw new Error('Error sending email', { cause: error });
76
67
  }
77
68
  }
69
+ /**
70
+ * Create an SMTP transporter with the current OAuth2 access token.
71
+ */
72
+ createSmtpTransporter(accessToken) {
73
+ return nodemailer.createTransport({
74
+ auth: {
75
+ accessToken,
76
+ accessUrl: process.env.TML_PROVIDER_EMAIL_AUTH_ACCESS_URL,
77
+ clientId: process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_ID,
78
+ clientSecret: process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_SECRET,
79
+ type: 'OAuth2',
80
+ user: process.env.TML_PROVIDER_EMAIL_AUTH_USER,
81
+ },
82
+ from: process.env.TML_PROVIDER_EMAIL_FROM,
83
+ host: process.env.TML_PROVIDER_EMAIL_SERVER_HOST,
84
+ port: Number(process.env.TML_PROVIDER_EMAIL_SERVER_PORT),
85
+ });
86
+ }
87
+ /**
88
+ * Fetch and cache the Microsoft OAuth2 access token.
89
+ */
90
+ async getRefreshToken() {
91
+ if (this._refreshToken.token && this._refreshToken.expiresAt && this._refreshToken.expiresAt > Date.now())
92
+ return this._refreshToken.token;
93
+ const requestBody = new URLSearchParams({
94
+ client_id: process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_ID,
95
+ client_secret: process.env.TML_PROVIDER_EMAIL_AUTH_CLIENT_SECRET,
96
+ grant_type: 'client_credentials',
97
+ scope: 'https://outlook.office365.com/.default',
98
+ }).toString();
99
+ const response = await fetch(process.env.TML_PROVIDER_EMAIL_AUTH_ACCESS_URL, {
100
+ body: requestBody,
101
+ headers: {
102
+ 'Content-Type': 'application/x-www-form-urlencoded',
103
+ },
104
+ method: 'POST',
105
+ });
106
+ const responseText = await response.text();
107
+ if (!response.ok) {
108
+ throw new Error(`Token request failed (${response.status}): ${responseText.slice(0, 500)}`);
109
+ }
110
+ const data = JSON.parse(responseText);
111
+ this._refreshToken = {
112
+ expiresAt: Date.now() + (data.expires_in * 1000),
113
+ token: data.access_token,
114
+ };
115
+ return this._refreshToken.token;
116
+ }
78
117
  }
79
118
  export const emailProvider = asyncSingletonProxy(EmailProvider);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmlmobilidade/emails",
3
- "version": "20260622.1325.20",
3
+ "version": "20260623.1548.1",
4
4
  "author": {
5
5
  "email": "iso@tmlmobilidade.pt",
6
6
  "name": "TML-ISO"