@webda/google-auth 4.0.0-beta.2 → 4.0.0-beta.4
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 +122 -0
- package/lib/google-auth.service.d.ts +125 -0
- package/lib/google-auth.service.d.ts.map +1 -0
- package/lib/google-auth.service.js +197 -0
- package/lib/google-auth.service.js.map +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/package.json +31 -35
- package/webda.module.json +184 -136
- package/lib/google-auth.d.ts +0 -125
- package/lib/google-auth.d.ts.map +0 -1
- package/lib/google-auth.js +0 -227
- package/lib/google-auth.js.map +0 -1
- package/lib/google-auth.spec.d.ts +0 -2
- package/lib/google-auth.spec.d.ts.map +0 -1
- package/lib/google-auth.spec.js +0 -131
- package/lib/google-auth.spec.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# @webda/google-auth module
|
|
2
|
+
|
|
3
|
+
This module is part of Webda Application Framework that allows you to quickly develop applications with all modern prerequisites: Security, Extensibility, GraphQL, REST, CloudNative [https://webda.io](https://webda.io)
|
|
4
|
+
|
|
5
|
+
<img src="https://webda.io/images/webda.svg" width="128" />
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
[](https://gitter.im/loopingz/webda?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
10
|
+
[](https://codecov.io/gh/loopingz/webda.io)
|
|
11
|
+
[](https://sonarcloud.io/summary/new_code?id=loopingz_webda.io)
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
<!-- README_HEADER -->
|
|
15
|
+
|
|
16
|
+
# @webda/google-auth
|
|
17
|
+
|
|
18
|
+
> "Sign in with Google" (OpenID Connect) provider for [`@webda/auth`](../auth): adds `GET /auth/google`, its callback
|
|
19
|
+
> and the `Auth.Google.Token` operation; logins go through the `Authentication` service (linking, registration,
|
|
20
|
+
> sessions, tokens).
|
|
21
|
+
|
|
22
|
+
## When to use it
|
|
23
|
+
|
|
24
|
+
- You want users to sign in with their Google account, in the browser or from a mobile/desktop client holding a
|
|
25
|
+
Google ID token.
|
|
26
|
+
- You want to restrict logins to a Google Workspace domain (`hostedDomain`).
|
|
27
|
+
- You need an offline Google refresh token (`access_type: "offline"`; stored in the ident tokens).
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm add @webda/auth @webda/google-auth
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Configuration
|
|
36
|
+
|
|
37
|
+
`GoogleAuthentication` is a provider service next to `Authentication` (see the
|
|
38
|
+
[Authentication guide](https://docs.webda.io/Security/Authentication)):
|
|
39
|
+
|
|
40
|
+
```jsonc
|
|
41
|
+
{
|
|
42
|
+
"services": {
|
|
43
|
+
"Authentication": { "type": "Webda/Authentication" },
|
|
44
|
+
"google": {
|
|
45
|
+
"type": "Webda/GoogleAuthentication",
|
|
46
|
+
"client_id": "${GOOGLE_CLIENT_ID}",
|
|
47
|
+
"client_secret": "${GOOGLE_CLIENT_SECRET}",
|
|
48
|
+
// Register exactly this url in the Google Cloud console; set it explicitly behind a proxy
|
|
49
|
+
"redirect_uri": "https://api.example.com/auth/google/callback",
|
|
50
|
+
"redirects": {
|
|
51
|
+
"success": "https://app.example.com/", // default "/"
|
|
52
|
+
"failure": "https://app.example.com/login" // required, receives ?reason=CODE
|
|
53
|
+
},
|
|
54
|
+
// Allowed targets of GET /auth/google?redirect=... (same origin, this path or below)
|
|
55
|
+
"authorized_uris": ["https://app.example.com/"],
|
|
56
|
+
"audiences": ["1234-ios.apps.googleusercontent.com"], // other client ids accepted by Auth.Google.Token
|
|
57
|
+
"hostedDomain": "example.com" // optional: Google Workspace accounts of this domain only
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Parameter | Default | Description |
|
|
64
|
+
| ----------------------------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
65
|
+
| `client_id`, `client_secret` | required | OAuth client of type "Web application" |
|
|
66
|
+
| `url` | `/auth/google` | Prefix of the login and callback routes |
|
|
67
|
+
| `redirect_uri` | request url + `/callback` | Callback url sent to Google |
|
|
68
|
+
| `redirects.success` / `redirects.failure` | `/` / required | Where the callback redirects |
|
|
69
|
+
| `authorized_uris` | `[]` | Allowed `redirect` parameters (absolute urls) |
|
|
70
|
+
| `scope` | `["openid", "email", "profile"]` | Requested scopes (`openid` is needed for the ID token) |
|
|
71
|
+
| `access_type` | `"online"` | `"offline"` also returns a refresh token |
|
|
72
|
+
| `audiences` | `[]` | Extra client ids accepted by `Auth.Google.Token` |
|
|
73
|
+
| `hostedDomain` | - | Required `hd` claim; refused with `EMAIL_DOMAIN_NOT_ALLOWED` |
|
|
74
|
+
| `auth_options` | - | Extra authorization url parameters (`prompt`, `login_hint`...); cannot override the flow fields |
|
|
75
|
+
| `allowedEmailDomains`, `trustEmailVerification` | - | Per-provider email policy of `@webda/auth` |
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
- Browser: link to `GET /auth/google?redirect=https://app.example.com/after`. The flow uses PKCE and an OpenID nonce,
|
|
80
|
+
kept in a short-lived encrypted `webda_oauth_google` cookie (so it also works with a `SameSite=Strict` session
|
|
81
|
+
cookie). After the Google consent the callback logs the user in and redirects to `redirect` (or
|
|
82
|
+
`redirects.success`), adding `?mfa=required` when the user still has to pass MFA. Failures redirect to
|
|
83
|
+
`redirects.failure?reason=CODE` (`STATE_MISMATCH`, `PROVIDER_ERROR`, `TOKEN_INVALID`, `ACCOUNT_EXISTS`,
|
|
84
|
+
`EMAIL_DOMAIN_NOT_ALLOWED`, ...).
|
|
85
|
+
- Other clients: `POST /auth/google/token` (`Auth.Google.Token`) with a JSON body `{ "token": "<Google ID token>" }`
|
|
86
|
+
or the v3 body `{ "tokens": { "id_token": "...", "access_token": "...", ... } }` returns the `@webda/auth` result
|
|
87
|
+
(`{ status: "ok", accessToken, refreshToken, ... }`). Only ID tokens are verified; access tokens alone are refused.
|
|
88
|
+
This operation never links the identity to an already logged-in user.
|
|
89
|
+
|
|
90
|
+
The Google identity is the ID token `sub`; the email is only treated as verified when `email_verified` is `true`.
|
|
91
|
+
Google credentials are stored encrypted on the ident (`await ident.tokens.get()`) and published after each login:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
useService("google").on("GoogleAuth.Tokens", async ({ tokens, context }) => {
|
|
95
|
+
// tokens.access_token, tokens.refresh_token (access_type "offline")
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Reference
|
|
100
|
+
|
|
101
|
+
- Source: [`packages/google-auth`](https://github.com/loopingz/webda.io/tree/main/packages/google-auth)
|
|
102
|
+
- Related: [`@webda/auth`](../auth) (`OAuthProvider` base class, `Authentication` service).
|
|
103
|
+
|
|
104
|
+
<!-- README_FOOTER -->
|
|
105
|
+
|
|
106
|
+
## Sponsors
|
|
107
|
+
|
|
108
|
+
<!--
|
|
109
|
+
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor](mailto:sponsor@webda.io)
|
|
110
|
+
-->
|
|
111
|
+
|
|
112
|
+
Arize AI is a machine learning observability and model monitoring platform. It helps you visualize, monitor, and explain your machine learning models. [Learn more](https://arize.com)
|
|
113
|
+
|
|
114
|
+
[<img src="https://arize.com/hubfs/arize/brand/arize-logomark-1.png" width="200">](https://arize.com)
|
|
115
|
+
|
|
116
|
+
Loopingz is a software development company that provides consulting and development services. [Learn more](https://loopingz.com)
|
|
117
|
+
|
|
118
|
+
[<img src="https://loopingz.com/images/logo.png" width="200">](https://loopingz.com)
|
|
119
|
+
|
|
120
|
+
Tellae is an innovative consulting firm specialized in cities transportation issues. We provide our clients, both public and private, with solutions to support your strategic and operational decisions. [Learn more](https://tellae.fr)
|
|
121
|
+
|
|
122
|
+
[<img src="https://tellae.fr/" width="200">](https://tellae.fr)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { type AuthResult, type EventWithContext, type ResolvedIdentity } from "@webda/core";
|
|
2
|
+
import { type OAuthAuthorizationRequest, type OAuthCallbackRequest, OAuthProvider, OAuthProviderParameters, type OAuthTokenRequest } from "@webda/auth";
|
|
3
|
+
import { type Credentials, OAuth2Client, type TokenPayload } from "google-auth-library";
|
|
4
|
+
/** Emitted with the Google credentials of a successful login */
|
|
5
|
+
export interface EventGoogleOAuthToken extends EventWithContext {
|
|
6
|
+
/**
|
|
7
|
+
* Credentials: from the code exchange (browser flow), or as sent to `Auth.Google.Token` (`tokens`, or
|
|
8
|
+
* `{ id_token }` for a bare token). Only the ID token is verified: treat the other values as client-provided on
|
|
9
|
+
* the token operation
|
|
10
|
+
*/
|
|
11
|
+
tokens: Credentials;
|
|
12
|
+
}
|
|
13
|
+
/** Events of GoogleAuthentication */
|
|
14
|
+
export type GoogleAuthEvents = {
|
|
15
|
+
"GoogleAuth.Tokens": EventGoogleOAuthToken;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Google login parameters
|
|
19
|
+
* https://developers.google.com/identity/protocols/oauth2/openid-connect
|
|
20
|
+
*/
|
|
21
|
+
export declare class GoogleParameters extends OAuthProviderParameters {
|
|
22
|
+
/**
|
|
23
|
+
* Google OAuth client id (web application)
|
|
24
|
+
*/
|
|
25
|
+
client_id: string;
|
|
26
|
+
/**
|
|
27
|
+
* Google OAuth client secret
|
|
28
|
+
*/
|
|
29
|
+
client_secret: string;
|
|
30
|
+
/**
|
|
31
|
+
* Scopes requested from Google
|
|
32
|
+
* @default ["openid", "email", "profile"]
|
|
33
|
+
*/
|
|
34
|
+
scope?: string[];
|
|
35
|
+
/**
|
|
36
|
+
* Type of access: "offline" also returns a refresh token (stored encrypted in the ident tokens)
|
|
37
|
+
* @default "online"
|
|
38
|
+
*/
|
|
39
|
+
access_type?: "online" | "offline";
|
|
40
|
+
/**
|
|
41
|
+
* Other client ids whose ID tokens `Auth.Google.Token` accepts (for example Android/iOS client ids)
|
|
42
|
+
* @default []
|
|
43
|
+
*/
|
|
44
|
+
audiences?: string[];
|
|
45
|
+
/**
|
|
46
|
+
* Only accept Google Workspace accounts of this domain (the `hd` claim of the ID token); also sent as a hint to
|
|
47
|
+
* the account chooser
|
|
48
|
+
*/
|
|
49
|
+
hostedDomain?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Additional authorization url parameters (for example `prompt`, `login_hint`); cannot override `state`,
|
|
52
|
+
* `redirect_uri`, `scope`, `access_type`, `response_type`, `code_challenge*`, `nonce` nor `hd`
|
|
53
|
+
* See https://developers.google.com/identity/protocols/oauth2/openid-connect#authenticationuriparameters
|
|
54
|
+
*/
|
|
55
|
+
auth_options?: Record<string, any>;
|
|
56
|
+
/**
|
|
57
|
+
* @param params - raw parameters
|
|
58
|
+
* @returns this
|
|
59
|
+
*/
|
|
60
|
+
load(params?: any): this;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Sign in with Google (OpenID Connect)
|
|
64
|
+
*
|
|
65
|
+
* The browser flow (`GET /auth/google`, `GET /auth/google/callback`) uses PKCE and a nonce, exchanges the code and
|
|
66
|
+
* verifies the returned ID token for `client_id`. `Auth.Google.Token` accepts a Google ID token, as `token` or as
|
|
67
|
+
* the `id_token` of `tokens` (v3 body), whose audience is `client_id` or one of `audiences` (never an access
|
|
68
|
+
* token). Credentials are stored encrypted on the ident and emitted with `GoogleAuth.Tokens` after the login.
|
|
69
|
+
* @WebdaModda GoogleAuthentication
|
|
70
|
+
*/
|
|
71
|
+
export declare class GoogleAuthentication<T extends GoogleParameters = GoogleParameters> extends OAuthProvider<T, GoogleAuthEvents> {
|
|
72
|
+
static Parameters: typeof GoogleParameters;
|
|
73
|
+
readonly providerName = "google";
|
|
74
|
+
/** Client verifying ID tokens: one per service so the Google certificates cache is reused */
|
|
75
|
+
protected verifier: OAuth2Client;
|
|
76
|
+
/** @override */
|
|
77
|
+
init(): Promise<this>;
|
|
78
|
+
/**
|
|
79
|
+
* @param redirectUri - callback url
|
|
80
|
+
* @returns a Google OAuth client for the authorization url and the code exchange
|
|
81
|
+
*/
|
|
82
|
+
protected getClient(redirectUri?: string): OAuth2Client;
|
|
83
|
+
/**
|
|
84
|
+
* @param request - authorization request
|
|
85
|
+
* @returns the Google authorization url
|
|
86
|
+
*/
|
|
87
|
+
getAuthorizationUrl(request: OAuthAuthorizationRequest): string;
|
|
88
|
+
/**
|
|
89
|
+
* Exchange the code with the PKCE verifier, verify the returned ID token for `client_id` and its nonce
|
|
90
|
+
* @param request - code exchange
|
|
91
|
+
* @returns the identity, with the credentials of the exchange
|
|
92
|
+
*/
|
|
93
|
+
handleCallback(request: OAuthCallbackRequest): Promise<ResolvedIdentity>;
|
|
94
|
+
/**
|
|
95
|
+
* Verify a Google ID token sent by a client, as `token` or `tokens.id_token`
|
|
96
|
+
* @param request - token request
|
|
97
|
+
* @returns the identity, with the credentials sent in `tokens`
|
|
98
|
+
*/
|
|
99
|
+
handleToken(request: OAuthTokenRequest): Promise<ResolvedIdentity>;
|
|
100
|
+
/**
|
|
101
|
+
* Emit `GoogleAuth.Tokens` once the login succeeded
|
|
102
|
+
* @param identity - the identity
|
|
103
|
+
* @param _result - the result
|
|
104
|
+
* @param source - browser callback or token operation
|
|
105
|
+
* @param request - the token operation request
|
|
106
|
+
*/
|
|
107
|
+
protected onAuthenticated(identity: ResolvedIdentity, _result: AuthResult, source: "callback" | "token", request?: OAuthTokenRequest): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Verify an ID token: signature, expiry, issuer (google-auth-library), audience and hosted domain
|
|
110
|
+
* @param idToken - ID token
|
|
111
|
+
* @param audiences - accepted audiences
|
|
112
|
+
* @returns the payload
|
|
113
|
+
* @throws TokenInvalid when the token does not verify
|
|
114
|
+
* @throws EmailDomainNotAllowed when `hostedDomain` is set and the token is not from that domain
|
|
115
|
+
*/
|
|
116
|
+
protected verifyIdToken(idToken: string, audiences: string[]): Promise<TokenPayload>;
|
|
117
|
+
/**
|
|
118
|
+
* @param payload - verified ID token payload
|
|
119
|
+
* @param tokens - credentials to store
|
|
120
|
+
* @returns the identity
|
|
121
|
+
*/
|
|
122
|
+
protected toIdentity(payload: TokenPayload, tokens?: Credentials): ResolvedIdentity;
|
|
123
|
+
}
|
|
124
|
+
export default GoogleAuthentication;
|
|
125
|
+
//# sourceMappingURL=google-auth.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-auth.service.d.ts","sourceRoot":"","sources":["../src/google-auth.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAA0B,MAAM,aAAa,CAAC;AACpH,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,aAAa,EACb,uBAAuB,EACvB,KAAK,iBAAiB,EAGvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,KAAK,WAAW,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE7G,gEAAgE;AAChE,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,qCAAqC;AACrC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,mBAAmB,EAAE,qBAAqB,CAAC;CAC5C,CAAC;AAEF;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,uBAAuB;IAC3D;;OAEG;IACK,SAAS,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACK,aAAa,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACK,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IACnC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEnC;;;OAGG;IACH,IAAI,CAAC,MAAM,GAAE,GAAQ,GAAG,IAAI,CAM3B;CACF;AAED;;;;;;;;GAQG;AACH,qBAAa,oBAAoB,CAAC,CAAC,SAAS,gBAAgB,GAAG,gBAAgB,CAAE,SAAQ,aAAa,CACpG,CAAC,EACD,gBAAgB,CACjB;IACC,MAAM,CAAC,UAAU,0BAAoB;IAErC,QAAQ,CAAC,YAAY,YAAY;IAEjC,6FAA6F;IAC7F,SAAS,CAAC,QAAQ,EAAE,YAAY,CAAC;IAEjC,gBAAgB;IACV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAI1B;IAED;;;OAGG;IACH,SAAS,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,YAAY,CAMtD;IAED;;;OAGG;IACH,mBAAmB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,CAc9D;IAED;;;;OAIG;IACG,cAAc,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAoB7E;IAED;;;;OAIG;IACG,WAAW,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAcvE;IAED;;;;;;OAMG;IACH,UAAgB,eAAe,CAC7B,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,UAAU,GAAG,OAAO,EAC5B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAGf;IAED;;;;;;;OAOG;IACH,UAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CA2BzF;IAED;;;;OAIG;IACH,SAAS,CAAC,UAAU,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,gBAAgB,CAUlF;CACF;eAcc,oBAAoB"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { useContext, WebdaError } from "@webda/core";
|
|
3
|
+
import { EmailDomainNotAllowed, OAuthProvider, OAuthProviderParameters, safeErrorReason, TokenInvalid } from "@webda/auth";
|
|
4
|
+
import { CodeChallengeMethod, OAuth2Client } from "google-auth-library";
|
|
5
|
+
/**
|
|
6
|
+
* Google login parameters
|
|
7
|
+
* https://developers.google.com/identity/protocols/oauth2/openid-connect
|
|
8
|
+
*/
|
|
9
|
+
export class GoogleParameters extends OAuthProviderParameters {
|
|
10
|
+
/**
|
|
11
|
+
* @param params - raw parameters
|
|
12
|
+
* @returns this
|
|
13
|
+
*/
|
|
14
|
+
load(params = {}) {
|
|
15
|
+
super.load(params);
|
|
16
|
+
this.access_type ?? (this.access_type = "online");
|
|
17
|
+
this.scope ?? (this.scope = ["openid", "email", "profile"]);
|
|
18
|
+
this.audiences ?? (this.audiences = []);
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Sign in with Google (OpenID Connect)
|
|
24
|
+
*
|
|
25
|
+
* The browser flow (`GET /auth/google`, `GET /auth/google/callback`) uses PKCE and a nonce, exchanges the code and
|
|
26
|
+
* verifies the returned ID token for `client_id`. `Auth.Google.Token` accepts a Google ID token, as `token` or as
|
|
27
|
+
* the `id_token` of `tokens` (v3 body), whose audience is `client_id` or one of `audiences` (never an access
|
|
28
|
+
* token). Credentials are stored encrypted on the ident and emitted with `GoogleAuth.Tokens` after the login.
|
|
29
|
+
* @WebdaModda GoogleAuthentication
|
|
30
|
+
*/
|
|
31
|
+
export class GoogleAuthentication extends OAuthProvider {
|
|
32
|
+
constructor() {
|
|
33
|
+
super(...arguments);
|
|
34
|
+
this.providerName = "google";
|
|
35
|
+
}
|
|
36
|
+
/** @override */
|
|
37
|
+
async init() {
|
|
38
|
+
await super.init();
|
|
39
|
+
this.verifier = new OAuth2Client({ clientId: this.parameters.client_id });
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* @param redirectUri - callback url
|
|
44
|
+
* @returns a Google OAuth client for the authorization url and the code exchange
|
|
45
|
+
*/
|
|
46
|
+
getClient(redirectUri) {
|
|
47
|
+
return new OAuth2Client({
|
|
48
|
+
clientId: this.parameters.client_id,
|
|
49
|
+
clientSecret: this.parameters.client_secret,
|
|
50
|
+
redirectUri
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* @param request - authorization request
|
|
55
|
+
* @returns the Google authorization url
|
|
56
|
+
*/
|
|
57
|
+
getAuthorizationUrl(request) {
|
|
58
|
+
const { auth_options, access_type, hostedDomain } = this.parameters;
|
|
59
|
+
return this.getClient(request.redirectUri).generateAuthUrl({
|
|
60
|
+
...(auth_options ?? {}),
|
|
61
|
+
access_type,
|
|
62
|
+
scope: request.scope,
|
|
63
|
+
state: request.state,
|
|
64
|
+
redirect_uri: request.redirectUri,
|
|
65
|
+
response_type: "code",
|
|
66
|
+
code_challenge_method: CodeChallengeMethod.S256,
|
|
67
|
+
code_challenge: request.codeChallenge,
|
|
68
|
+
nonce: request.nonce,
|
|
69
|
+
...(hostedDomain ? { hd: hostedDomain } : {})
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Exchange the code with the PKCE verifier, verify the returned ID token for `client_id` and its nonce
|
|
74
|
+
* @param request - code exchange
|
|
75
|
+
* @returns the identity, with the credentials of the exchange
|
|
76
|
+
*/
|
|
77
|
+
async handleCallback(request) {
|
|
78
|
+
let tokens;
|
|
79
|
+
try {
|
|
80
|
+
({ tokens } = await this.getClient(request.redirectUri).getToken({
|
|
81
|
+
code: request.code,
|
|
82
|
+
codeVerifier: request.codeVerifier,
|
|
83
|
+
redirect_uri: request.redirectUri
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
this.log("WARN", "Google code exchange failed:", safeErrorReason(err));
|
|
88
|
+
throw new TokenInvalid("Code exchange failed");
|
|
89
|
+
}
|
|
90
|
+
if (!tokens?.id_token) {
|
|
91
|
+
throw new TokenInvalid("Google returned no ID token: the 'openid' scope is required");
|
|
92
|
+
}
|
|
93
|
+
const payload = await this.verifyIdToken(tokens.id_token, [this.parameters.client_id]);
|
|
94
|
+
if (typeof payload.nonce !== "string" || !sameSecret(payload.nonce, request.nonce)) {
|
|
95
|
+
throw new TokenInvalid("ID token nonce mismatch");
|
|
96
|
+
}
|
|
97
|
+
return this.toIdentity(payload, tokens);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Verify a Google ID token sent by a client, as `token` or `tokens.id_token`
|
|
101
|
+
* @param request - token request
|
|
102
|
+
* @returns the identity, with the credentials sent in `tokens`
|
|
103
|
+
*/
|
|
104
|
+
async handleToken(request) {
|
|
105
|
+
const fromTokens = request.tokens?.id_token;
|
|
106
|
+
if (request.token && fromTokens !== undefined && fromTokens !== request.token) {
|
|
107
|
+
throw new WebdaError.BadRequest("token and tokens.id_token differ");
|
|
108
|
+
}
|
|
109
|
+
const idToken = request.token ?? fromTokens;
|
|
110
|
+
if (typeof idToken !== "string" || !idToken) {
|
|
111
|
+
throw new TokenInvalid("A Google ID token is required");
|
|
112
|
+
}
|
|
113
|
+
const payload = await this.verifyIdToken(idToken, [
|
|
114
|
+
this.parameters.client_id,
|
|
115
|
+
...(this.parameters.audiences ?? [])
|
|
116
|
+
]);
|
|
117
|
+
return this.toIdentity(payload, request.tokens);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Emit `GoogleAuth.Tokens` once the login succeeded
|
|
121
|
+
* @param identity - the identity
|
|
122
|
+
* @param _result - the result
|
|
123
|
+
* @param source - browser callback or token operation
|
|
124
|
+
* @param request - the token operation request
|
|
125
|
+
*/
|
|
126
|
+
async onAuthenticated(identity, _result, source, request) {
|
|
127
|
+
const tokens = source === "token" ? (request?.tokens ?? { id_token: request?.token }) : identity.tokens;
|
|
128
|
+
await this.emit("GoogleAuth.Tokens", { tokens, context: useContext() });
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Verify an ID token: signature, expiry, issuer (google-auth-library), audience and hosted domain
|
|
132
|
+
* @param idToken - ID token
|
|
133
|
+
* @param audiences - accepted audiences
|
|
134
|
+
* @returns the payload
|
|
135
|
+
* @throws TokenInvalid when the token does not verify
|
|
136
|
+
* @throws EmailDomainNotAllowed when `hostedDomain` is set and the token is not from that domain
|
|
137
|
+
*/
|
|
138
|
+
async verifyIdToken(idToken, audiences) {
|
|
139
|
+
const audience = audiences.filter(a => typeof a === "string" && a);
|
|
140
|
+
// Without audience the library would skip the check: never verify without one
|
|
141
|
+
if (!audience.length) {
|
|
142
|
+
throw new TokenInvalid("No audience configured");
|
|
143
|
+
}
|
|
144
|
+
this.verifier ?? (this.verifier = new OAuth2Client({ clientId: this.parameters.client_id }));
|
|
145
|
+
let payload;
|
|
146
|
+
try {
|
|
147
|
+
const ticket = await this.verifier.verifyIdToken({
|
|
148
|
+
idToken,
|
|
149
|
+
audience: audience.length === 1 ? audience[0] : audience
|
|
150
|
+
});
|
|
151
|
+
payload = ticket.getPayload();
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
// The library messages contain the token or its payload: only the error class is logged
|
|
155
|
+
this.log("WARN", "Google ID token verification failed:", safeErrorReason(err));
|
|
156
|
+
throw new TokenInvalid();
|
|
157
|
+
}
|
|
158
|
+
if (!payload?.sub) {
|
|
159
|
+
throw new TokenInvalid("ID token has no subject");
|
|
160
|
+
}
|
|
161
|
+
const hostedDomain = this.parameters.hostedDomain?.trim().toLowerCase();
|
|
162
|
+
if (hostedDomain && payload.hd?.toLowerCase() !== hostedDomain) {
|
|
163
|
+
throw new EmailDomainNotAllowed("Google account is not from the hosted domain");
|
|
164
|
+
}
|
|
165
|
+
return payload;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* @param payload - verified ID token payload
|
|
169
|
+
* @param tokens - credentials to store
|
|
170
|
+
* @returns the identity
|
|
171
|
+
*/
|
|
172
|
+
toIdentity(payload, tokens) {
|
|
173
|
+
return {
|
|
174
|
+
provider: this.providerName,
|
|
175
|
+
providerUid: payload.sub,
|
|
176
|
+
email: payload.email,
|
|
177
|
+
emailVerified: payload.email_verified === true,
|
|
178
|
+
profile: { name: payload.name, picture: payload.picture, locale: payload.locale, hd: payload.hd },
|
|
179
|
+
tokens,
|
|
180
|
+
amr: ["oauth"]
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
GoogleAuthentication.Parameters = GoogleParameters;
|
|
185
|
+
/**
|
|
186
|
+
* Constant-time comparison
|
|
187
|
+
* @param a - received value
|
|
188
|
+
* @param b - expected value
|
|
189
|
+
* @returns true when equal
|
|
190
|
+
*/
|
|
191
|
+
function sameSecret(a, b) {
|
|
192
|
+
const left = Buffer.from(a);
|
|
193
|
+
const right = Buffer.from(b);
|
|
194
|
+
return left.length === right.length && timingSafeEqual(left, right);
|
|
195
|
+
}
|
|
196
|
+
export default GoogleAuthentication;
|
|
197
|
+
//# sourceMappingURL=google-auth.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-auth.service.js","sourceRoot":"","sources":["../src/google-auth.service.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./google-auth.js";
|
|
1
|
+
export * from "./google-auth.service.js";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./google-auth.js";
|
|
1
|
+
export * from "./google-auth.service.js";
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webda/google-auth",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.4",
|
|
4
4
|
"description": "Webda Google Authentication",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"build": "webda build",
|
|
8
|
-
"build:watch": "webda build --watch",
|
|
9
|
-
"pretest": "npm run build",
|
|
10
|
-
"lint": "eslint",
|
|
11
|
-
"lint:fix": "eslint --fix",
|
|
12
|
-
"test": "c8 mocha",
|
|
13
|
-
"format": "prettier --check src/**/*",
|
|
14
|
-
"format:fix": "prettier --write src/**/*"
|
|
15
|
-
},
|
|
16
6
|
"repository": {
|
|
17
7
|
"type": "git",
|
|
18
8
|
"url": "git+https://github.com/loopingz/webda.io.git"
|
|
@@ -33,41 +23,47 @@
|
|
|
33
23
|
},
|
|
34
24
|
"homepage": "https://github.com/loopingz/webda.io#readme",
|
|
35
25
|
"dependencies": {
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
"c8": {
|
|
40
|
-
"report-dir": "./reports",
|
|
41
|
-
"reporter": [
|
|
42
|
-
"html",
|
|
43
|
-
"lcov",
|
|
44
|
-
"json",
|
|
45
|
-
"text"
|
|
46
|
-
],
|
|
47
|
-
"exclude": [
|
|
48
|
-
"**/*.spec.ts"
|
|
49
|
-
]
|
|
26
|
+
"google-auth-library": "^10.6.2",
|
|
27
|
+
"@webda/auth": "4.0.0-beta.4",
|
|
28
|
+
"@webda/core": "4.0.0-beta.4"
|
|
50
29
|
},
|
|
51
30
|
"publishConfig": {
|
|
52
31
|
"access": "public"
|
|
53
32
|
},
|
|
54
33
|
"devDependencies": {
|
|
55
|
-
"@
|
|
56
|
-
"@
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
34
|
+
"@types/node": "25.5.0",
|
|
35
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
36
|
+
"jscodeshift": "^17.3.0",
|
|
37
|
+
"skott": "^0.35.8",
|
|
38
|
+
"tsx": "^4.21.0",
|
|
39
|
+
"typescript": "7.1.0-dev.20260929.1",
|
|
40
|
+
"vite": "^6.0.0",
|
|
41
|
+
"vitest": "^4.1.2",
|
|
42
|
+
"@webda/compiler": "^4.0.0-beta.4",
|
|
43
|
+
"@webda/content-mapper": "^4.0.0-beta.4",
|
|
44
|
+
"@webda/test": "4.0.0-beta.3",
|
|
45
|
+
"@webda/workout": "4.0.0-beta.3"
|
|
61
46
|
},
|
|
62
47
|
"type": "module",
|
|
63
48
|
"engines": {
|
|
64
|
-
"node": ">=
|
|
49
|
+
"node": ">=22.0.0"
|
|
65
50
|
},
|
|
66
51
|
"nx": {
|
|
67
52
|
"implicitDependencies": [
|
|
68
53
|
"@webda/core",
|
|
69
|
-
"@webda/
|
|
54
|
+
"@webda/auth"
|
|
70
55
|
]
|
|
71
56
|
},
|
|
72
|
-
"
|
|
73
|
-
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "webdac build",
|
|
59
|
+
"build:watch": "webdac build --watch",
|
|
60
|
+
"pretest": "npm run build",
|
|
61
|
+
"lint": "eslint",
|
|
62
|
+
"lint:fix": "eslint --fix",
|
|
63
|
+
"test": "vitest run",
|
|
64
|
+
"format": "prettier --check src/**/*",
|
|
65
|
+
"format:fix": "prettier --write src/**/*",
|
|
66
|
+
"deps:graph": "skott --showCircularDependencies --no-trackTypeOnlyDependencies --displayMode=raw -e .ts",
|
|
67
|
+
"codemod": "jscodeshift --parser=ts --extensions=tsx,ts -t ../codemod/current.mjs src"
|
|
68
|
+
}
|
|
69
|
+
}
|