homebridge-meural-cognito 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 +20 -0
- package/README.md +134 -0
- package/config.schema.json +61 -0
- package/dist/auth/cognito.d.ts +33 -0
- package/dist/auth/cognito.d.ts.map +1 -0
- package/dist/auth/cognito.js +130 -0
- package/dist/auth/cognito.js.map +1 -0
- package/dist/auth/netgearAuth.d.ts +44 -0
- package/dist/auth/netgearAuth.d.ts.map +1 -0
- package/dist/auth/netgearAuth.js +187 -0
- package/dist/auth/netgearAuth.js.map +1 -0
- package/dist/auth/redact.d.ts +3 -0
- package/dist/auth/redact.d.ts.map +1 -0
- package/dist/auth/redact.js +29 -0
- package/dist/auth/redact.js.map +1 -0
- package/dist/auth/tokenStore.d.ts +20 -0
- package/dist/auth/tokenStore.d.ts.map +1 -0
- package/dist/auth/tokenStore.js +50 -0
- package/dist/auth/tokenStore.js.map +1 -0
- package/dist/http.d.ts +23 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +42 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/meuralApi.d.ts +19 -0
- package/dist/meuralApi.d.ts.map +1 -0
- package/dist/meuralApi.js +55 -0
- package/dist/meuralApi.js.map +1 -0
- package/dist/platform.d.ts +49 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +193 -0
- package/dist/platform.js.map +1 -0
- package/dist/platformAccessory.d.ts +82 -0
- package/dist/platformAccessory.d.ts.map +1 -0
- package/dist/platformAccessory.js +485 -0
- package/dist/platformAccessory.js.map +1 -0
- package/dist/settings.d.ts +9 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +12 -0
- package/dist/settings.js.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2012-2018 Scott Chacon and others
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Homebridge Meural Cognito
|
|
2
|
+
|
|
3
|
+
Maintained edition of Mike Knoop's `homebridge-meural` plugin. It preserves the original HomeKit accessories and local canvas controls while replacing Meural's retired legacy login with the current Netgear Accounts and AWS Cognito flow.
|
|
4
|
+
|
|
5
|
+
The original MIT license and Git history are retained. Upstream: <https://github.com/mikeknoop/homebridge-meural>.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Canvas discovery through `https://api.meural.com/v1`
|
|
10
|
+
- Netgear Accounts / AWS Cognito `CUSTOM_AUTH`
|
|
11
|
+
- `USER_PASSWORD_AUTH` fallback used by account migration
|
|
12
|
+
- Email, SMS, software-token, and custom challenge responses
|
|
13
|
+
- Automatic access-token refresh
|
|
14
|
+
- Refresh-token persistence with file mode `0600`
|
|
15
|
+
- Existing HomeKit power, brightness, remote, and Show Random controls
|
|
16
|
+
- Docker image for amd64 and arm64-capable Node base images
|
|
17
|
+
|
|
18
|
+
## Architecture
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
HomeKit -> Homebridge plugin -> Meural v1 cloud API
|
|
22
|
+
|
|
|
23
|
+
+-> Netgear Accounts -> AWS Cognito
|
|
24
|
+
|
|
|
25
|
+
+-> Canvas LAN HTTP controls
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Cloud authentication and discovery use HTTPS. Power, brightness, remote keys, and artwork selection continue to call each canvas on the local network, matching the upstream plugin.
|
|
29
|
+
|
|
30
|
+
## Authentication
|
|
31
|
+
|
|
32
|
+
The production web client currently:
|
|
33
|
+
|
|
34
|
+
1. Starts Cognito `CUSTOM_AUTH` in `eu-west-1`.
|
|
35
|
+
2. Answers password and, when required, OTP/MFA custom challenges.
|
|
36
|
+
3. Falls back to `USER_PASSWORD_AUTH` for the observed account-migration error.
|
|
37
|
+
4. Exchanges the Cognito access token for a Netgear authorization code.
|
|
38
|
+
5. Exchanges that code for Meural access, ID, and refresh tokens.
|
|
39
|
+
6. Sends `Authorization: Token <access token>` to the Meural API.
|
|
40
|
+
7. Refreshes through Netgear Accounts with the Meural refresh token and public app key.
|
|
41
|
+
|
|
42
|
+
No PKCE, browser cookie, CSRF token, hosted UI, or SRP exchange was observed in the Meural web flow. See [docs/auth-flow.md](docs/auth-flow.md).
|
|
43
|
+
|
|
44
|
+
Credentials are needed only when no usable token file exists. After the first successful login, remove `MEURAL_PASSWORD` and `MEURAL_CHALLENGE_RESPONSE` from the environment.
|
|
45
|
+
|
|
46
|
+
## Homebridge Configuration
|
|
47
|
+
|
|
48
|
+
Install `homebridge-meural-cognito` from the Homebridge UI plugin search, then configure:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"platform": "MeuralCanvas",
|
|
53
|
+
"account_email": "",
|
|
54
|
+
"account_password": "",
|
|
55
|
+
"token_path": "/homebridge/meural/tokens.json",
|
|
56
|
+
"control_center_remote": true,
|
|
57
|
+
"exclude_devices": []
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
For backward compatibility, `account_email` and `account_password` still work. Environment variables take precedence and are preferred:
|
|
62
|
+
|
|
63
|
+
| Variable | Default |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `MEURAL_USERNAME` | Homebridge `account_email` |
|
|
66
|
+
| `MEURAL_PASSWORD` | Homebridge `account_password` |
|
|
67
|
+
| `MEURAL_CHALLENGE_RESPONSE` | unset |
|
|
68
|
+
| `MEURAL_TOKEN_PATH` | Homebridge storage directory |
|
|
69
|
+
| `MEURAL_API_BASE_URL` | `https://api.meural.com` |
|
|
70
|
+
| `MEURAL_AWS_REGION` | discovered public value |
|
|
71
|
+
| `MEURAL_COGNITO_CLIENT_ID` | discovered public value |
|
|
72
|
+
| `MEURAL_COGNITO_USER_POOL_ID` | discovered public value |
|
|
73
|
+
|
|
74
|
+
The public AWS and application identifiers are built in, so normal deployments do not need to set them.
|
|
75
|
+
|
|
76
|
+
## Docker Compose
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
cp .env.example .env
|
|
80
|
+
mkdir -p homebridge
|
|
81
|
+
cp examples/config.json homebridge/config.json
|
|
82
|
+
docker compose build
|
|
83
|
+
docker compose up -d
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Put the username and password in `.env` for the first start. If Netgear requests an OTP, set `MEURAL_CHALLENGE_RESPONSE`, restart once, then clear it. After `homebridge/meural/tokens.json` exists, clear the password as well.
|
|
87
|
+
|
|
88
|
+
The container:
|
|
89
|
+
|
|
90
|
+
- runs as the unprivileged `node` user
|
|
91
|
+
- mounts `/homebridge` for configuration, Homebridge state, and tokens
|
|
92
|
+
- uses host networking for HomeKit/mDNS discovery
|
|
93
|
+
- includes a Homebridge TCP health check
|
|
94
|
+
- adds `no-new-privileges`
|
|
95
|
+
|
|
96
|
+
## Synology
|
|
97
|
+
|
|
98
|
+
In Container Manager, create a project from `docker-compose.yml`, bind a persistent folder to `/homebridge`, and use host networking. Ensure the mounted folder is writable by UID/GID `1000`. Use project environment variables or a protected `.env` file for the one-time login.
|
|
99
|
+
|
|
100
|
+
## Token Security
|
|
101
|
+
|
|
102
|
+
- Token directories are mode `0700`; token files are mode `0600`.
|
|
103
|
+
- Writes use a temporary file followed by an atomic rename.
|
|
104
|
+
- Passwords are never written to the token file.
|
|
105
|
+
- Tokens and authorization values are redacted by logging helpers.
|
|
106
|
+
- `.env`, tokens, cookies, browser profiles, and HAR files are ignored by Git and Docker.
|
|
107
|
+
|
|
108
|
+
Filesystem permissions protect the token file; it is not application-encrypted. Use an encrypted host volume when storage-at-rest encryption is required.
|
|
109
|
+
|
|
110
|
+
## Development
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm ci
|
|
114
|
+
npm run check
|
|
115
|
+
docker build -t homebridge-meural-cognito:local .
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Tests use mocked Cognito, Netgear, Meural, and token-store responses. They do not modify a Meural account or canvas.
|
|
119
|
+
|
|
120
|
+
## Troubleshooting
|
|
121
|
+
|
|
122
|
+
- `Meural login is required`: provide credentials for one start or restore the token volume.
|
|
123
|
+
- `Cognito challenge ... requires an interactive response`: set the one-time `MEURAL_CHALLENGE_RESPONSE`.
|
|
124
|
+
- Refresh failure: the token file is cleared and a new login is required.
|
|
125
|
+
- Canvas cloud discovery works but controls fail: verify Homebridge can reach the canvas local IP.
|
|
126
|
+
- HomeKit cannot discover the bridge: use host networking and confirm TCP port `51826` plus mDNS are not blocked.
|
|
127
|
+
|
|
128
|
+
## Compatibility
|
|
129
|
+
|
|
130
|
+
See [docs/compatibility-matrix.md](docs/compatibility-matrix.md). The plugin interface remains `MeuralCanvas`; no separate proxy API or second plugin fork is required.
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pluginAlias": "MeuralCanvas",
|
|
3
|
+
"pluginType": "platform",
|
|
4
|
+
"singular": true,
|
|
5
|
+
"schema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"platform": {
|
|
9
|
+
"title": "Platform",
|
|
10
|
+
"type": "string",
|
|
11
|
+
"required": true,
|
|
12
|
+
"default": "MeuralCanvas"
|
|
13
|
+
},
|
|
14
|
+
"account_email": {
|
|
15
|
+
"title": "Account Email",
|
|
16
|
+
"type": "string",
|
|
17
|
+
"required": true,
|
|
18
|
+
"default": ""
|
|
19
|
+
},
|
|
20
|
+
"account_password": {
|
|
21
|
+
"title": "Account Password",
|
|
22
|
+
"type": "string",
|
|
23
|
+
"required": false,
|
|
24
|
+
"format": "password",
|
|
25
|
+
"default": ""
|
|
26
|
+
},
|
|
27
|
+
"token_path": {
|
|
28
|
+
"title": "Token File Path",
|
|
29
|
+
"type": "string",
|
|
30
|
+
"required": false,
|
|
31
|
+
"default": ""
|
|
32
|
+
},
|
|
33
|
+
"api_base_url": {
|
|
34
|
+
"title": "Meural API Base URL",
|
|
35
|
+
"type": "string",
|
|
36
|
+
"required": false,
|
|
37
|
+
"default": "https://api.meural.com"
|
|
38
|
+
},
|
|
39
|
+
"accounts_base_url": {
|
|
40
|
+
"title": "Netgear Accounts Base URL",
|
|
41
|
+
"type": "string",
|
|
42
|
+
"required": false,
|
|
43
|
+
"default": "https://accounts2.netgear.com"
|
|
44
|
+
},
|
|
45
|
+
"exclude_devices": {
|
|
46
|
+
"title": "Exclude Devices",
|
|
47
|
+
"type": "array",
|
|
48
|
+
"items": {
|
|
49
|
+
"type": "string"
|
|
50
|
+
},
|
|
51
|
+
"required": false
|
|
52
|
+
},
|
|
53
|
+
"control_center_remote": {
|
|
54
|
+
"title": "Enable Control Center Remote",
|
|
55
|
+
"type": "boolean",
|
|
56
|
+
"required": false,
|
|
57
|
+
"default": true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { HttpTransport } from '../http';
|
|
2
|
+
export interface CognitoTokens {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
idToken?: string;
|
|
5
|
+
refreshToken?: string;
|
|
6
|
+
expiresIn?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface CognitoChallenge {
|
|
9
|
+
name: string;
|
|
10
|
+
parameters: Record<string, string>;
|
|
11
|
+
attempt: number;
|
|
12
|
+
}
|
|
13
|
+
export type ChallengeHandler = (challenge: CognitoChallenge) => Promise<string>;
|
|
14
|
+
export interface CognitoClientConfig {
|
|
15
|
+
region: string;
|
|
16
|
+
clientId: string;
|
|
17
|
+
metadata?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
export declare class CognitoClient {
|
|
20
|
+
private readonly config;
|
|
21
|
+
private readonly transport;
|
|
22
|
+
constructor(config: CognitoClientConfig, transport: HttpTransport);
|
|
23
|
+
authenticate(username: string, password: string, challengeHandler?: ChallengeHandler): Promise<CognitoTokens>;
|
|
24
|
+
refresh(refreshToken: string): Promise<CognitoTokens>;
|
|
25
|
+
private initiate;
|
|
26
|
+
private respond;
|
|
27
|
+
private call;
|
|
28
|
+
private passwordAnswersChallenge;
|
|
29
|
+
private resolveChallenge;
|
|
30
|
+
private responseKey;
|
|
31
|
+
private isUserMigrationError;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=cognito.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cognito.d.ts","sourceRoot":"","sources":["../../src/auth/cognito.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,aAAa,EAAE,MAAM,SAAS,CAAC;AAE1D,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,gBAAgB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAchF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,qBAAa,aAAa;IAEtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,MAAM,EAAE,mBAAmB,EAC3B,SAAS,EAAE,aAAa;IAGrC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC;IAwC7G,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;YAe7C,QAAQ;YASR,OAAO;YAmBP,IAAI;IAalB,OAAO,CAAC,wBAAwB;YAQlB,gBAAgB;IAgB9B,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,oBAAoB;CAM7B"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CognitoClient = void 0;
|
|
4
|
+
const http_1 = require("../http");
|
|
5
|
+
class CognitoClient {
|
|
6
|
+
constructor(config, transport) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
this.transport = transport;
|
|
9
|
+
}
|
|
10
|
+
async authenticate(username, password, challengeHandler) {
|
|
11
|
+
let response;
|
|
12
|
+
try {
|
|
13
|
+
response = await this.initiate('CUSTOM_AUTH', {
|
|
14
|
+
USERNAME: username,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (!this.isUserMigrationError(error)) {
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
response = await this.initiate('USER_PASSWORD_AUTH', {
|
|
22
|
+
USERNAME: username,
|
|
23
|
+
PASSWORD: password,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
let attempt = 0;
|
|
27
|
+
while (!response.AuthenticationResult) {
|
|
28
|
+
attempt += 1;
|
|
29
|
+
const challengeName = response.ChallengeName;
|
|
30
|
+
if (!challengeName || !response.Session) {
|
|
31
|
+
throw new Error('Cognito returned neither tokens nor a supported challenge');
|
|
32
|
+
}
|
|
33
|
+
const parameters = response.ChallengeParameters ?? {};
|
|
34
|
+
const answer = this.passwordAnswersChallenge(challengeName, parameters, attempt)
|
|
35
|
+
? password
|
|
36
|
+
: await this.resolveChallenge(challengeName, parameters, attempt, challengeHandler);
|
|
37
|
+
response = await this.respond(username, challengeName, answer, response.Session);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
accessToken: response.AuthenticationResult.AccessToken,
|
|
41
|
+
idToken: response.AuthenticationResult.IdToken,
|
|
42
|
+
refreshToken: response.AuthenticationResult.RefreshToken,
|
|
43
|
+
expiresIn: response.AuthenticationResult.ExpiresIn,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async refresh(refreshToken) {
|
|
47
|
+
const response = await this.initiate('REFRESH_TOKEN_AUTH', {
|
|
48
|
+
REFRESH_TOKEN: refreshToken,
|
|
49
|
+
});
|
|
50
|
+
if (!response.AuthenticationResult) {
|
|
51
|
+
throw new Error('Cognito refresh did not return tokens');
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
accessToken: response.AuthenticationResult.AccessToken,
|
|
55
|
+
idToken: response.AuthenticationResult.IdToken,
|
|
56
|
+
refreshToken,
|
|
57
|
+
expiresIn: response.AuthenticationResult.ExpiresIn,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async initiate(authFlow, authParameters) {
|
|
61
|
+
return this.call('AWSCognitoIdentityProviderService.InitiateAuth', {
|
|
62
|
+
AuthFlow: authFlow,
|
|
63
|
+
ClientId: this.config.clientId,
|
|
64
|
+
AuthParameters: authParameters,
|
|
65
|
+
ClientMetadata: this.config.metadata,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async respond(username, challengeName, answer, session) {
|
|
69
|
+
const responseKey = this.responseKey(challengeName);
|
|
70
|
+
return this.call('AWSCognitoIdentityProviderService.RespondToAuthChallenge', {
|
|
71
|
+
ChallengeName: challengeName,
|
|
72
|
+
ClientId: this.config.clientId,
|
|
73
|
+
Session: session,
|
|
74
|
+
ChallengeResponses: {
|
|
75
|
+
USERNAME: username,
|
|
76
|
+
[responseKey]: answer,
|
|
77
|
+
},
|
|
78
|
+
ClientMetadata: this.config.metadata,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async call(target, data) {
|
|
82
|
+
const response = await this.transport.request({
|
|
83
|
+
method: 'POST',
|
|
84
|
+
url: `https://cognito-idp.${this.config.region}.amazonaws.com/`,
|
|
85
|
+
headers: {
|
|
86
|
+
'Content-Type': 'application/x-amz-json-1.1',
|
|
87
|
+
'X-Amz-Target': target,
|
|
88
|
+
},
|
|
89
|
+
data,
|
|
90
|
+
});
|
|
91
|
+
return response.data;
|
|
92
|
+
}
|
|
93
|
+
passwordAnswersChallenge(name, parameters, attempt) {
|
|
94
|
+
if (name !== 'CUSTOM_CHALLENGE' || attempt !== 1) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const description = JSON.stringify(parameters).toLowerCase();
|
|
98
|
+
return !/(otp|verification|email|phone|code)/.test(description);
|
|
99
|
+
}
|
|
100
|
+
async resolveChallenge(name, parameters, attempt, challengeHandler) {
|
|
101
|
+
if (!challengeHandler) {
|
|
102
|
+
throw new Error(`Cognito challenge ${name} requires an interactive response`);
|
|
103
|
+
}
|
|
104
|
+
const answer = await challengeHandler({ name, parameters, attempt });
|
|
105
|
+
if (!answer) {
|
|
106
|
+
throw new Error(`Cognito challenge ${name} was not answered`);
|
|
107
|
+
}
|
|
108
|
+
return answer;
|
|
109
|
+
}
|
|
110
|
+
responseKey(challengeName) {
|
|
111
|
+
switch (challengeName) {
|
|
112
|
+
case 'SMS_MFA':
|
|
113
|
+
return 'SMS_MFA_CODE';
|
|
114
|
+
case 'SOFTWARE_TOKEN_MFA':
|
|
115
|
+
return 'SOFTWARE_TOKEN_MFA_CODE';
|
|
116
|
+
case 'NEW_PASSWORD_REQUIRED':
|
|
117
|
+
return 'NEW_PASSWORD';
|
|
118
|
+
default:
|
|
119
|
+
return 'ANSWER';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
isUserMigrationError(error) {
|
|
123
|
+
if (!(error instanceof http_1.HttpRequestError)) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
return JSON.stringify(error.responseData).includes('User_Not_Found');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
exports.CognitoClient = CognitoClient;
|
|
130
|
+
//# sourceMappingURL=cognito.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cognito.js","sourceRoot":"","sources":["../../src/auth/cognito.ts"],"names":[],"mappings":";;;AAAA,kCAA0D;AAmC1D,MAAa,aAAa;IACxB,YACmB,MAA2B,EAC3B,SAAwB;QADxB,WAAM,GAAN,MAAM,CAAqB;QAC3B,cAAS,GAAT,SAAS,CAAe;IACxC,CAAC;IAEJ,KAAK,CAAC,YAAY,CAAC,QAAgB,EAAE,QAAgB,EAAE,gBAAmC;QACxF,IAAI,QAAyB,CAAC;QAC9B,IAAI;YACF,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE;gBAC5C,QAAQ,EAAE,QAAQ;aACnB,CAAC,CAAC;SACJ;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE;gBACrC,MAAM,KAAK,CAAC;aACb;YACD,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,oBAAoB,EAAE;gBACnD,QAAQ,EAAE,QAAQ;gBAClB,QAAQ,EAAE,QAAQ;aACnB,CAAC,CAAC;SACJ;QAED,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,OAAO,CAAC,QAAQ,CAAC,oBAAoB,EAAE;YACrC,OAAO,IAAI,CAAC,CAAC;YACb,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC;YAC7C,IAAI,CAAC,aAAa,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE;gBACvC,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;aAC9E;YAED,MAAM,UAAU,GAAG,QAAQ,CAAC,mBAAmB,IAAI,EAAE,CAAC;YACtD,MAAM,MAAM,GAAG,IAAI,CAAC,wBAAwB,CAAC,aAAa,EAAE,UAAU,EAAE,OAAO,CAAC;gBAC9E,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC;YAEtF,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;SAClF;QAED,OAAO;YACL,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC,WAAW;YACtD,OAAO,EAAE,QAAQ,CAAC,oBAAoB,CAAC,OAAO;YAC9C,YAAY,EAAE,QAAQ,CAAC,oBAAoB,CAAC,YAAY;YACxD,SAAS,EAAE,QAAQ,CAAC,oBAAoB,CAAC,SAAS;SACnD,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,YAAoB;QAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,oBAAoB,EAAE;YACzD,aAAa,EAAE,YAAY;SAC5B,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,oBAAoB,EAAE;YAClC,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;SAC1D;QACD,OAAO;YACL,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC,WAAW;YACtD,OAAO,EAAE,QAAQ,CAAC,oBAAoB,CAAC,OAAO;YAC9C,YAAY;YACZ,SAAS,EAAE,QAAQ,CAAC,oBAAoB,CAAC,SAAS;SACnD,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,cAAsC;QAC7E,OAAO,IAAI,CAAC,IAAI,CAAC,gDAAgD,EAAE;YACjE,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC9B,cAAc,EAAE,cAAc;YAC9B,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;SACrC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,QAAgB,EAChB,aAAqB,EACrB,MAAc,EACd,OAAe;QAEf,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,IAAI,CAAC,0DAA0D,EAAE;YAC3E,aAAa,EAAE,aAAa;YAC5B,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC9B,OAAO,EAAE,OAAO;YAChB,kBAAkB,EAAE;gBAClB,QAAQ,EAAE,QAAQ;gBAClB,CAAC,WAAW,CAAC,EAAE,MAAM;aACtB;YACD,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;SACrC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,IAAa;QAC9C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAkB;YAC7D,MAAM,EAAE,MAAM;YACd,GAAG,EAAE,uBAAuB,IAAI,CAAC,MAAM,CAAC,MAAM,iBAAiB;YAC/D,OAAO,EAAE;gBACP,cAAc,EAAE,4BAA4B;gBAC5C,cAAc,EAAE,MAAM;aACvB;YACD,IAAI;SACL,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAEO,wBAAwB,CAAC,IAAY,EAAE,UAAkC,EAAE,OAAe;QAChG,IAAI,IAAI,KAAK,kBAAkB,IAAI,OAAO,KAAK,CAAC,EAAE;YAChD,OAAO,KAAK,CAAC;SACd;QACD,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7D,OAAO,CAAC,qCAAqC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClE,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAC5B,IAAY,EACZ,UAAkC,EAClC,OAAe,EACf,gBAAmC;QAEnC,IAAI,CAAC,gBAAgB,EAAE;YACrB,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,mCAAmC,CAAC,CAAC;SAC/E;QACD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;QACrE,IAAI,CAAC,MAAM,EAAE;YACX,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,mBAAmB,CAAC,CAAC;SAC/D;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,WAAW,CAAC,aAAqB;QACvC,QAAQ,aAAa,EAAE;YACrB,KAAK,SAAS;gBACZ,OAAO,cAAc,CAAC;YACxB,KAAK,oBAAoB;gBACvB,OAAO,yBAAyB,CAAC;YACnC,KAAK,uBAAuB;gBAC1B,OAAO,cAAc,CAAC;YACxB;gBACE,OAAO,QAAQ,CAAC;SACnB;IACH,CAAC;IAEO,oBAAoB,CAAC,KAAc;QACzC,IAAI,CAAC,CAAC,KAAK,YAAY,uBAAgB,CAAC,EAAE;YACxC,OAAO,KAAK,CAAC;SACd;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IACvE,CAAC;CACF;AAjJD,sCAiJC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ChallengeHandler } from './cognito';
|
|
2
|
+
import { TokenStore } from './tokenStore';
|
|
3
|
+
import { HttpTransport } from '../http';
|
|
4
|
+
export declare const DEFAULT_AUTH_CONFIG: {
|
|
5
|
+
awsRegion: string;
|
|
6
|
+
cognitoUserPoolId: string;
|
|
7
|
+
cognitoClientId: string;
|
|
8
|
+
meuralClientId: string;
|
|
9
|
+
accountsBaseUrl: string;
|
|
10
|
+
};
|
|
11
|
+
export interface NetgearAuthConfig {
|
|
12
|
+
username?: string;
|
|
13
|
+
password?: string;
|
|
14
|
+
awsRegion?: string;
|
|
15
|
+
cognitoUserPoolId?: string;
|
|
16
|
+
cognitoClientId?: string;
|
|
17
|
+
meuralClientId?: string;
|
|
18
|
+
accountsBaseUrl?: string;
|
|
19
|
+
challengeResponse?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare class ReauthenticationRequiredError extends Error {
|
|
22
|
+
constructor(message?: string);
|
|
23
|
+
}
|
|
24
|
+
export declare class NetgearAuthProvider {
|
|
25
|
+
private readonly tokenStore;
|
|
26
|
+
private readonly transport;
|
|
27
|
+
private tokens?;
|
|
28
|
+
private refreshPromise?;
|
|
29
|
+
private readonly resolvedConfig;
|
|
30
|
+
private readonly cognito;
|
|
31
|
+
constructor(config: NetgearAuthConfig, tokenStore: TokenStore, transport: HttpTransport);
|
|
32
|
+
getAccessToken(forceRefresh?: boolean): Promise<string>;
|
|
33
|
+
login(challengeHandler?: ChallengeHandler): Promise<void>;
|
|
34
|
+
clear(): Promise<void>;
|
|
35
|
+
private refresh;
|
|
36
|
+
private performRefresh;
|
|
37
|
+
private loadTokens;
|
|
38
|
+
private isExpired;
|
|
39
|
+
private toStoredTokens;
|
|
40
|
+
private pick;
|
|
41
|
+
private jwtExpiration;
|
|
42
|
+
private environmentChallengeHandler;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=netgearAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"netgearAuth.d.ts","sourceRoot":"","sources":["../../src/auth/netgearAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAgB,UAAU,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxC,eAAO,MAAM,mBAAmB;;;;;;CAM/B,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAiBD,qBAAa,6BAA8B,SAAQ,KAAK;gBAC1C,OAAO,SAA6B;CAIjD;AAED,qBAAa,mBAAmB;IAW5B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAX5B,OAAO,CAAC,MAAM,CAAC,CAAe;IAC9B,OAAO,CAAC,cAAc,CAAC,CAAwB;IAE/C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAC4C;IAE3E,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;gBAGtC,MAAM,EAAE,iBAAiB,EACR,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,aAAa;IAwBrC,cAAc,CAAC,YAAY,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAarD,KAAK,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwCzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAKd,OAAO;YAYP,cAAc;YAoBd,UAAU;IAMxB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,IAAI;IAcZ,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,2BAA2B;CAOpC"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NetgearAuthProvider = exports.ReauthenticationRequiredError = exports.DEFAULT_AUTH_CONFIG = void 0;
|
|
4
|
+
const cognito_1 = require("./cognito");
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
exports.DEFAULT_AUTH_CONFIG = {
|
|
7
|
+
awsRegion: 'eu-west-1',
|
|
8
|
+
cognitoUserPoolId: 'eu-west-1_eHc7fzKFg',
|
|
9
|
+
cognitoClientId: '487bd4kvb1fnop6mbgk8gu5ibf',
|
|
10
|
+
meuralClientId: '3ui6nklcaqoij8inrkm06gfk4s',
|
|
11
|
+
accountsBaseUrl: 'https://accounts2.netgear.com',
|
|
12
|
+
};
|
|
13
|
+
class ReauthenticationRequiredError extends Error {
|
|
14
|
+
constructor(message = 'Meural login is required') {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = 'ReauthenticationRequiredError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.ReauthenticationRequiredError = ReauthenticationRequiredError;
|
|
20
|
+
class NetgearAuthProvider {
|
|
21
|
+
constructor(config, tokenStore, transport) {
|
|
22
|
+
this.tokenStore = tokenStore;
|
|
23
|
+
this.transport = transport;
|
|
24
|
+
this.resolvedConfig = {
|
|
25
|
+
username: config.username,
|
|
26
|
+
password: config.password,
|
|
27
|
+
challengeResponse: config.challengeResponse,
|
|
28
|
+
awsRegion: config.awsRegion || exports.DEFAULT_AUTH_CONFIG.awsRegion,
|
|
29
|
+
cognitoUserPoolId: config.cognitoUserPoolId || exports.DEFAULT_AUTH_CONFIG.cognitoUserPoolId,
|
|
30
|
+
cognitoClientId: config.cognitoClientId || exports.DEFAULT_AUTH_CONFIG.cognitoClientId,
|
|
31
|
+
meuralClientId: config.meuralClientId || exports.DEFAULT_AUTH_CONFIG.meuralClientId,
|
|
32
|
+
accountsBaseUrl: config.accountsBaseUrl || exports.DEFAULT_AUTH_CONFIG.accountsBaseUrl,
|
|
33
|
+
};
|
|
34
|
+
this.cognito = new cognito_1.CognitoClient({
|
|
35
|
+
region: this.resolvedConfig.awsRegion,
|
|
36
|
+
clientId: this.resolvedConfig.cognitoClientId,
|
|
37
|
+
metadata: {
|
|
38
|
+
trustID: (0, crypto_1.randomUUID)(),
|
|
39
|
+
sourceEvent: 'login',
|
|
40
|
+
language: 'en-US',
|
|
41
|
+
appType: 'meural',
|
|
42
|
+
},
|
|
43
|
+
}, transport);
|
|
44
|
+
}
|
|
45
|
+
async getAccessToken(forceRefresh = false) {
|
|
46
|
+
await this.loadTokens();
|
|
47
|
+
if (!this.tokens) {
|
|
48
|
+
await this.login();
|
|
49
|
+
}
|
|
50
|
+
else if (forceRefresh || this.isExpired(this.tokens)) {
|
|
51
|
+
await this.refresh();
|
|
52
|
+
}
|
|
53
|
+
if (!this.tokens) {
|
|
54
|
+
throw new ReauthenticationRequiredError();
|
|
55
|
+
}
|
|
56
|
+
return this.tokens.accessToken;
|
|
57
|
+
}
|
|
58
|
+
async login(challengeHandler) {
|
|
59
|
+
const username = this.resolvedConfig.username;
|
|
60
|
+
const password = this.resolvedConfig.password;
|
|
61
|
+
if (!username || !password) {
|
|
62
|
+
throw new ReauthenticationRequiredError('No persisted Meural session was found. Supply account credentials once to create one.');
|
|
63
|
+
}
|
|
64
|
+
const handler = challengeHandler ?? this.environmentChallengeHandler();
|
|
65
|
+
const cognitoTokens = await this.cognito.authenticate(username, password, handler);
|
|
66
|
+
const authorizeUrl = new URL('/api/oauth/authorize', this.resolvedConfig.accountsBaseUrl);
|
|
67
|
+
authorizeUrl.searchParams.set('client_id', this.resolvedConfig.meuralClientId);
|
|
68
|
+
const authorizeResponse = await this.transport.request({
|
|
69
|
+
method: 'GET',
|
|
70
|
+
url: authorizeUrl.toString(),
|
|
71
|
+
headers: {
|
|
72
|
+
'Authorization': `Bearer ${cognitoTokens.accessToken}`,
|
|
73
|
+
'Accept': 'application/json',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
const code = this.pick(authorizeResponse.data, ['code', 'authorizationCode']);
|
|
77
|
+
if (!code) {
|
|
78
|
+
throw new Error('Netgear authorization did not return an authorization code');
|
|
79
|
+
}
|
|
80
|
+
const tokenUrl = new URL('/api/oauth/token', this.resolvedConfig.accountsBaseUrl);
|
|
81
|
+
tokenUrl.searchParams.set('code', code);
|
|
82
|
+
const tokenResponse = await this.transport.request({
|
|
83
|
+
method: 'GET',
|
|
84
|
+
url: tokenUrl.toString(),
|
|
85
|
+
headers: {
|
|
86
|
+
'Accept': 'application/json',
|
|
87
|
+
'Content-Type': 'application/json',
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
this.tokens = this.toStoredTokens(tokenResponse.data);
|
|
91
|
+
await this.tokenStore.save(this.tokens);
|
|
92
|
+
}
|
|
93
|
+
async clear() {
|
|
94
|
+
this.tokens = undefined;
|
|
95
|
+
await this.tokenStore.clear();
|
|
96
|
+
}
|
|
97
|
+
async refresh() {
|
|
98
|
+
if (!this.tokens?.refreshToken) {
|
|
99
|
+
throw new ReauthenticationRequiredError('The Meural refresh token is missing');
|
|
100
|
+
}
|
|
101
|
+
if (!this.refreshPromise) {
|
|
102
|
+
this.refreshPromise = this.performRefresh().finally(() => {
|
|
103
|
+
this.refreshPromise = undefined;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
this.tokens = await this.refreshPromise;
|
|
107
|
+
}
|
|
108
|
+
async performRefresh() {
|
|
109
|
+
try {
|
|
110
|
+
const response = await this.transport.request({
|
|
111
|
+
method: 'GET',
|
|
112
|
+
url: new URL('/api/getAccessToken', this.resolvedConfig.accountsBaseUrl).toString(),
|
|
113
|
+
headers: {
|
|
114
|
+
'Authorization': `Bearer ${this.tokens.refreshToken}`,
|
|
115
|
+
'appkey': this.resolvedConfig.meuralClientId,
|
|
116
|
+
'Accept': 'application/json',
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
const refreshed = this.toStoredTokens(response.data, this.tokens.refreshToken);
|
|
120
|
+
await this.tokenStore.save(refreshed);
|
|
121
|
+
return refreshed;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
await this.clear();
|
|
125
|
+
throw new ReauthenticationRequiredError('The Meural session expired and a new login is required');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async loadTokens() {
|
|
129
|
+
if (this.tokens === undefined) {
|
|
130
|
+
this.tokens = await this.tokenStore.load();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
isExpired(tokens) {
|
|
134
|
+
return tokens.expiresAt <= Date.now() + 60000;
|
|
135
|
+
}
|
|
136
|
+
toStoredTokens(response, existingRefreshToken) {
|
|
137
|
+
const body = response.data ?? response;
|
|
138
|
+
const accessToken = this.pick(body, ['access_token', 'accessToken', 'token']);
|
|
139
|
+
const refreshToken = this.pick(body, ['refresh_token', 'refreshToken']) ?? existingRefreshToken;
|
|
140
|
+
if (!accessToken || !refreshToken) {
|
|
141
|
+
throw new Error('Netgear token response was missing required tokens');
|
|
142
|
+
}
|
|
143
|
+
const expiresIn = Number(this.pick(body, ['expires_in', 'expiresIn']) ?? 3600);
|
|
144
|
+
return {
|
|
145
|
+
version: 1,
|
|
146
|
+
accessToken,
|
|
147
|
+
idToken: this.pick(body, ['id_token', 'idToken']),
|
|
148
|
+
refreshToken,
|
|
149
|
+
expiresAt: this.jwtExpiration(accessToken) ?? Date.now() + expiresIn * 1000,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
pick(response, keys) {
|
|
153
|
+
const body = response.data ?? response;
|
|
154
|
+
for (const key of keys) {
|
|
155
|
+
const value = body[key];
|
|
156
|
+
if (typeof value === 'string') {
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
if (typeof value === 'number') {
|
|
160
|
+
return String(value);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
jwtExpiration(token) {
|
|
166
|
+
const parts = token.split('.');
|
|
167
|
+
if (parts.length !== 3) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
|
|
172
|
+
return payload.exp ? payload.exp * 1000 : undefined;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
environmentChallengeHandler() {
|
|
179
|
+
const answer = this.resolvedConfig.challengeResponse;
|
|
180
|
+
if (!answer) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
return async () => answer;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
exports.NetgearAuthProvider = NetgearAuthProvider;
|
|
187
|
+
//# sourceMappingURL=netgearAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"netgearAuth.js","sourceRoot":"","sources":["../../src/auth/netgearAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/auth/redact.ts"],"names":[],"mappings":"AAGA,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAiB9C;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKvD"}
|