diff --git a/keyserver/package.json b/keyserver/package.json --- a/keyserver/package.json +++ b/keyserver/package.json @@ -53,6 +53,7 @@ "express": "^4.17.3", "express-ws": "^4.0.0", "firebase-admin": "^10.1.0", + "gaxios": "^5.1.0", "geoip-lite": "^1.4.5", "invariant": "^2.2.4", "landing": "0.0.1", @@ -62,7 +63,6 @@ "mysql2": "^2.3.3", "node-schedule": "^2.1.0", "nodemailer": "^6.6.1", - "rust-node-addon": "0.0.1", "react": "18.1.0", "react-dom": "18.1.0", "react-html-email": "^3.0.0", @@ -72,6 +72,7 @@ "redux": "^4.0.4", "replacestream": "^4.0.3", "rereadable-stream": "^1.4.5", + "rust-node-addon": "0.0.1", "sharp": "^0.30.5", "siwe": "^1.1.6", "sql-template-strings": "^2.2.2", diff --git a/keyserver/src/push/providers.js b/keyserver/src/push/providers.js --- a/keyserver/src/push/providers.js +++ b/keyserver/src/push/providers.js @@ -4,7 +4,9 @@ import type { Provider as APNProvider } from '@parse/node-apn'; import fcmAdmin from 'firebase-admin'; import type { FirebaseApp } from 'firebase-admin'; +import { request } from 'gaxios'; import invariant from 'invariant'; +import url from 'url'; import webpush from 'web-push'; import type { PlatformDetails } from 'lib/types/device-types'; @@ -122,6 +124,61 @@ await getWebPushConfig(); } +type WNSConfig = { +tenantID: string, +appID: string, +secret: string }; +type WNSAccessToken = { +token: string, +expires: number }; +let wnsAccessToken: ?WNSAccessToken = null; +async function getWNSToken(): Promise { + const expiryWindowInMs = 30_000; + if ( + wnsAccessToken && + wnsAccessToken.expires >= Date.now() - expiryWindowInMs + ) { + return wnsAccessToken.token; + } + + const config = await importJSON({ + folder: 'secrets', + name: 'wns_config', + }); + + if (!config) { + return null; + } + + const data = url + .format({ + query: { + grant_type: 'client_credentials', + client_id: config.appID, + client_secret: config.secret, + scope: 'https://wns.windows.com/.default', + }, + }) + .substring(1); // strip leading ? + + try { + const response = await request({ + url: `https://login.microsoftonline.com/${config.tenantID}/oauth2/v2.0/token`, + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': data.length, + }, + data, + }); + + wnsAccessToken = { + token: response.data['access_token'], + expires: Date.now() + response.data['expires_in'] * 1000, + }; + } catch (err) { + console.error(err); + return null; + } + + return wnsAccessToken?.token; +} + export { getAPNPushProfileForCodeVersion, getFCMPushProfileForCodeVersion, @@ -132,4 +189,5 @@ getAPNsNotificationTopic, getWebPushConfig, ensureWebPushInitialized, + getWNSToken, }; diff --git a/yarn.lock b/yarn.lock --- a/yarn.lock +++ b/yarn.lock @@ -12062,6 +12062,16 @@ is-stream "^2.0.0" node-fetch "^2.6.1" +gaxios@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.1.0.tgz#133b77b45532be71eec72012b7e97c2320b6140a" + integrity sha512-aezGIjb+/VfsJtIcHGcBSerNEDdfdHeMros+RbYbGpmonKWQCOVOes0LVZhn1lDtIgq55qq0HaxymIoae3Fl/A== + dependencies: + extend "^3.0.2" + https-proxy-agent "^5.0.0" + is-stream "^2.0.0" + node-fetch "^2.6.7" + gcp-metadata@^4.2.0: version "4.3.1" resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.3.1.tgz#fb205fe6a90fef2fd9c85e6ba06e5559ee1eefa9"