Skip to content

Commit c133936

Browse files
authored
feat: add additional oauth attributes including prompt, login-hint, etc (#41)
1 parent 5b641de commit c133936

File tree

5 files changed

+132
-39
lines changed

5 files changed

+132
-39
lines changed

README.md

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -180,22 +180,26 @@ by using the component attributes mapped to the corresponding methods of
180180

181181
#### Attributes
182182

183-
| Name | Type | Description |
184-
| ---------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
185-
| `app-id` | `string` | The Google Drive app ID. See [`PickerBuilder.setAppId`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setappid). |
186-
| `client-id` | `string` | The OAuth 2.0 client ID. See [Using OAuth 2.0 to Access Google APIs](https://developers.google.com/identity/protocols/oauth2). |
187-
| `developer-key` | `string` | The API key for accessing Google Picker API. See [`PickerBuilder.setDeveloperKey`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setdeveloperkey). |
188-
| `hide-title-bar` | `"default"\|"true"\|"false"` | Hides the title bar of the picker if set to true. See [`PickerBuilder.hideTitleBar`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.hidetitlebar). |
189-
| `locale` | `string` | The locale to use for the picker. See [`PickerBuilder.setLocale`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setlocale). |
190-
| `max-items` | `number` | The maximum number of items that can be selected. See [`PickerBuilder.setMaxItems`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setmaxitems). |
191-
| `mine-only` | `boolean` | If set to true, only shows files owned by the user. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
192-
| `multiselect` | `boolean` | Enables multiple file selection if set to true. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
193-
| `nav-hidden` | `boolean` | Hides the navigation pane if set to true. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
194-
| `oauth-token` | `string` | The OAuth 2.0 token for authentication. See [`PickerBuilder.setOAuthToken`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setoauthtoken). |
195-
| `origin` | `string` | The origin parameter for the picker. See [`PickerBuilder.setOrigin`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setorigin). |
196-
| `relay-url` | `string` | The relay URL for the picker. See [`PickerBuilder.setRelayUrl`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setrelayurl). |
197-
| `scope` | `string` | The OAuth 2.0 scope for the picker. The default is `https://www.googleapis.com/auth/drive.file`. See [Drive API scopes](https://developers.google.com/drive/api/guides/api-specific-auth#drive-scopes). |
198-
| `title` | `string` | The title of the picker. See [`PickerBuilder.setTitle`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.settitle). |
183+
| Name | Type | Description |
184+
| ------------------------ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
185+
| `app-id` | `string` | The Google Drive app ID. See [`PickerBuilder.setAppId`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setappid). |
186+
| `client-id` | `string` | The OAuth 2.0 client ID. See [Using OAuth 2.0 to Access Google APIs](https://developers.google.com/identity/protocols/oauth2). |
187+
| `developer-key` | `string` | The API key for accessing Google Picker API. See [`PickerBuilder.setDeveloperKey`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setdeveloperkey). |
188+
| `hide-title-bar` | `"default"\|"true"\|"false"` | Hides the title bar of the picker if set to true. See [`PickerBuilder.hideTitleBar`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.hidetitlebar). |
189+
| `locale` | `string` | The locale to use for the picker. See [`PickerBuilder.setLocale`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setlocale). |
190+
| `max-items` | `number` | The maximum number of items that can be selected. See [`PickerBuilder.setMaxItems`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setmaxitems). |
191+
| `mine-only` | `boolean` | If set to true, only shows files owned by the user. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
192+
| `multiselect` | `boolean` | Enables multiple file selection if set to true. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
193+
| `nav-hidden` | `boolean` | Hides the navigation pane if set to true. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
194+
| `oauth-token` | `string` | The OAuth 2.0 token for authentication. See [`PickerBuilder.setOAuthToken`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setoauthtoken). |
195+
| `origin` | `string` | The origin parameter for the picker. See [`PickerBuilder.setOrigin`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setorigin). |
196+
| `relay-url` | `string` | The relay URL for the picker. See [`PickerBuilder.setRelayUrl`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setrelayurl). |
197+
| `scope` | `string` | The OAuth 2.0 scope for the picker. The default is `https://www.googleapis.com/auth/drive.file`. See [Drive API scopes](https://developers.google.com/drive/api/guides/api-specific-auth#drive-scopes). |
198+
| `title` | `string` | The title of the picker. See [`PickerBuilder.setTitle`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.settitle). |
199+
| `hd` | `string` | The hosted domain to restrict sign-in to. (Optional) See the `hd` field in the OpenID Connect docs. |
200+
| `include-granted-scopes` | `boolean` | Enables applications to use incremental authorization. See [`TokenClientConfig.include_granted_scopes`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig). |
201+
| `login-hint` | `string` | An email address or an ID token 'sub' value. Google will use the value as a hint of which user to sign in. See the `login_hint` field in the OpenID Connect docs. |
202+
| `prompt` | `""\|"none"\|"consent"\|"select_account"` | A space-delimited, case-sensitive list of prompts to present the user. See [`TokenClientConfig.prompt`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig) |
199203

200204
#### Events
201205

@@ -215,9 +219,15 @@ by using the component attributes mapped to the corresponding methods of
215219

216220
#### Properties
217221

218-
| Name | Type | Description |
219-
| --------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
220-
| `visible` | `boolean` | Controls the visibility of the picker after the picker dialog has been
closed. If any of the attributes change, the picker will be rebuilt and
the visibility will be reset. |
222+
| Name | Type | Description |
223+
| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
224+
| `visible` | `boolean` | Controls the visibility of the picker after the picker dialog has been
closed. If any of the attributes change, the picker will be rebuilt and
the visibility will be reset. |
225+
| `tokenClientConfig` | \`Omit< | |
226+
227+
```
228+
google.accounts.oauth2.TokenClientConfig,
229+
"callback" \| "error_callback" >` | |
230+
```
221231

222232
### `<drive-picker-docs-view/>`
223233

custom-elements.json

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,15 @@
221221
"privacy": "public",
222222
"description": "Controls the visibility of the picker after the picker dialog has been\nclosed. If any of the attributes change, the picker will be rebuilt and\nthe visibility will be reset."
223223
},
224+
{
225+
"kind": "field",
226+
"name": "tokenClientConfig",
227+
"type": {
228+
"text": "Omit<\n\t\tgoogle.accounts.oauth2.TokenClientConfig,\n\t\t\"callback\" | \"error_callback\"\n\t>"
229+
},
230+
"privacy": "public",
231+
"readonly": true
232+
},
224233
{
225234
"kind": "method",
226235
"name": "build",
@@ -251,7 +260,7 @@
251260
},
252261
{
253262
"kind": "method",
254-
"name": "retrieveAccessToken",
263+
"name": "requestAccessToken",
255264
"privacy": "private",
256265
"return": {
257266
"type": {
@@ -407,6 +416,34 @@
407416
"text": "string"
408417
},
409418
"description": "The title of the picker. See [`PickerBuilder.setTitle`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.settitle)."
419+
},
420+
{
421+
"type": {
422+
"text": "string"
423+
},
424+
"description": "The hosted domain to restrict sign-in to. (Optional) See the `hd` field in the OpenID Connect docs.",
425+
"name": "hd"
426+
},
427+
{
428+
"type": {
429+
"text": "boolean"
430+
},
431+
"description": "Enables applications to use incremental authorization. See [`TokenClientConfig.include_granted_scopes`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig).",
432+
"name": "include-granted-scopes"
433+
},
434+
{
435+
"type": {
436+
"text": "string"
437+
},
438+
"description": "An email address or an ID token 'sub' value. Google will use the value as a hint of which user to sign in. See the `login_hint` field in the OpenID Connect docs.",
439+
"name": "login-hint"
440+
},
441+
{
442+
"type": {
443+
"text": "\"\"|\"none\"|\"consent\"|\"select_account\""
444+
},
445+
"description": "A space-delimited, case-sensitive list of prompts to present the user. See [`TokenClientConfig.prompt`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig)",
446+
"name": "prompt"
410447
}
411448
],
412449
"superclass": {

src/drive-picker/drive-picker-element.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
getBoolAttr,
1919
getNumberAttribute,
2020
loadApi,
21-
retrieveAccessToken,
21+
requestAccessToken,
2222
setBoolAttrWithDefault,
2323
} from "../utils";
2424

@@ -77,6 +77,10 @@ declare global {
7777
* @attr {string} relay-url - The relay URL for the picker. See [`PickerBuilder.setRelayUrl`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setrelayurl).
7878
* @attr {string} scope - The OAuth 2.0 scope for the picker. The default is `https://www.googleapis.com/auth/drive.file`. See [Drive API scopes](https://developers.google.com/drive/api/guides/api-specific-auth#drive-scopes).
7979
* @attr {string} title - The title of the picker. See [`PickerBuilder.setTitle`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.settitle).
80+
* @attr {string} hd - The hosted domain to restrict sign-in to. (Optional) See the `hd` field in the OpenID Connect docs.
81+
* @attr {boolean} include-granted-scopes - Enables applications to use incremental authorization. See [`TokenClientConfig.include_granted_scopes`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig).
82+
* @attr {string} login-hint - An email address or an ID token 'sub' value. Google will use the value as a hint of which user to sign in. See the `login_hint` field in the OpenID Connect docs.
83+
* @attr {""|"none"|"consent"|"select_account"} prompt - A space-delimited, case-sensitive list of prompts to present the user. See [`TokenClientConfig.prompt`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig)
8084
*
8185
* @example
8286
*
@@ -130,6 +134,32 @@ export class DrivePickerElement extends HTMLElement {
130134
this.picker?.setVisible(value);
131135
}
132136

137+
public get tokenClientConfig(): Omit<
138+
google.accounts.oauth2.TokenClientConfig,
139+
"callback" | "error_callback"
140+
> {
141+
const clientId = this.getAttribute("client-id");
142+
const scope =
143+
this.getAttribute("scope") ??
144+
"https://www.googleapis.com/auth/drive.file";
145+
146+
if (!clientId || !scope) {
147+
throw new Error("client-id and scope are required attributes");
148+
}
149+
150+
return {
151+
client_id: clientId,
152+
hd: this.getAttribute("hd") ?? undefined,
153+
include_granted_scopes: Boolean(
154+
this.getAttribute("include-granted-scope"),
155+
),
156+
login_hint: this.getAttribute("login-hint") ?? undefined,
157+
prompt: (this.getAttribute("prompt") ??
158+
"") as google.accounts.oauth2.TokenClientConfig["prompt"],
159+
scope,
160+
};
161+
}
162+
133163
attributeChangedCallback() {
134164
this.build();
135165
return;
@@ -181,7 +211,7 @@ export class DrivePickerElement extends HTMLElement {
181211

182212
// OAuth token is required either as an attribute or from the OAuth flow using the client ID and scope
183213
const oauthToken =
184-
this.getAttribute("oauth-token") ?? (await this.retrieveAccessToken());
214+
this.getAttribute("oauth-token") ?? (await this.requestAccessToken());
185215

186216
if (!oauthToken) return;
187217

@@ -269,13 +299,8 @@ export class DrivePickerElement extends HTMLElement {
269299
);
270300
}
271301

272-
private retrieveAccessToken(): Promise<string | undefined> {
273-
return retrieveAccessToken(
274-
// biome-ignore lint/style/noNonNullAssertion: just let the error bubble up when null
275-
this.getAttribute("client-id")!,
276-
this.getAttribute("scope") ??
277-
"https://www.googleapis.com/auth/drive.file",
278-
)
302+
private async requestAccessToken(): Promise<string | undefined> {
303+
return requestAccessToken(this.tokenClientConfig)
279304
.then((response) => {
280305
const { access_token: token } = response;
281306
if (!token) {

src/stories/utils/manifest-helpers.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,32 @@ export const elementArgTypes: {
6767
case "number":
6868
inputType.control = "number";
6969
break;
70-
case '"default"|"true"|"false"':
71-
inputType.control = "select";
72-
inputType.options = ["default", "true", "false"];
73-
break;
7470
default:
75-
throw new Error(`Unsupported type: ${attr.type?.text}`);
71+
// check if string literal union such as "default"|"true"|"false"
72+
if (
73+
attr.type?.text.match(/"[a-zA-Z-0-9-_"]*"/) &&
74+
attr.type?.text.includes("|")
75+
) {
76+
inputType.control = "select";
77+
inputType.options = attr.type?.text.replace(/"/g, "").split("|");
78+
} else {
79+
throw new Error(`Unsupported type: ${attr.type?.text}`);
80+
}
81+
}
82+
83+
if (
84+
tagName === "drive-picker" &&
85+
[
86+
"client-id",
87+
"scope",
88+
"hd",
89+
"prompt",
90+
"login-hint",
91+
"include-granted-scopes",
92+
].includes(attr.name)
93+
) {
94+
// biome-ignore lint/style/noNonNullAssertion: <explanation>
95+
inputType!.table!.category = "OAuth";
7696
}
7797

7898
return [attr.name, inputType];

src/utils.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,18 @@ export class ClientConfigError extends Error {
3737
}
3838
}
3939

40-
export async function retrieveAccessToken(
41-
clientId: string,
42-
scope: string,
40+
export async function requestAccessToken(
41+
tokenClientConfig: Omit<
42+
google.accounts.oauth2.TokenClientConfig,
43+
"callback" | "error_callback"
44+
>,
4345
): Promise<google.accounts.oauth2.TokenResponse> {
4446
if (!window.google?.accounts?.oauth2) {
4547
await injectScript(GSI_URL);
4648
}
4749
return new Promise((resolve, reject) => {
4850
const client = window.google.accounts.oauth2.initTokenClient({
49-
client_id: clientId,
50-
scope: scope.replace(/,/g, " ").replace(/\s+/g, " "),
51+
...tokenClientConfig,
5152
callback: resolve,
5253
error_callback: reject,
5354
});

0 commit comments

Comments
 (0)