@shware/security 2.2.0 → 2.2.2

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 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/github.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport type { OAuth2Token, Provider } from '../types';\nimport { OAuth2Error } from '../error';\nimport { createAuthorizationUri, exchangeAuthorizationCode } from './common';\n\nexport function createGithubProvider(): Provider {\n return {\n authorizationUri: 'https://github.com/login/oauth/authorize',\n tokenUri: 'https://github.com/login/oauth/access_token',\n userInfoUri: 'https://api.github.com/user',\n userNameAttribute: 'id',\n defaultScope: ['user:email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GithubErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GithubToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n const response = await fetch(this.userInfoUri, { headers });\n if (!response.ok) {\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to fetch user info');\n }\n const data = (await response.json()) as GithubUserInfo;\n const emailResponse = await fetch('https://api.github.com/user/emails', { headers });\n if (!emailResponse.ok) {\n throw new OAuth2Error(\n emailResponse.status,\n 'invalid_request',\n 'Failed to fetch user email'\n );\n }\n const emails = (await emailResponse.json()) as GithubEmail[];\n data.email = emails.find((email) => email.primary)?.email || data.email;\n const email_verified = emails.find((email) => email.email === data.email)?.verified || false;\n return {\n data,\n claims: {\n sub: data.id,\n name: data.name || data.login,\n email_verified,\n email: data.email,\n picture: data.avatar_url,\n profile: data.html_url,\n website: data.html_url,\n updated_at: data.updated_at ? Date.parse(data.updated_at) : undefined,\n },\n };\n },\n };\n}\n\nexport const github = createGithubProvider();\n\nexport interface GithubUserInfo {\n login: string;\n id: string;\n node_id: string;\n avatar_url: string;\n gravatar_id: string;\n url: string;\n html_url: string;\n followers_url: string;\n following_url: string;\n gists_url: string;\n starred_url: string;\n subscriptions_url: string;\n organizations_url: string;\n repos_url: string;\n events_url: string;\n received_events_url: string;\n type: string;\n site_admin: boolean;\n name: string;\n company: string;\n blog: string;\n location: string;\n email: string;\n hireable: boolean;\n bio: string;\n twitter_username: string;\n public_repos: string;\n public_gists: string;\n followers: string;\n following: string;\n created_at: string;\n updated_at: string;\n private_gists: string;\n total_private_repos: string;\n owned_private_repos: string;\n disk_usage: string;\n collaborators: string;\n two_factor_authentication: boolean;\n plan: {\n name: string;\n space: string;\n private_repos: string;\n collaborators: string;\n };\n}\n\ninterface GithubEmail {\n email: string;\n primary: boolean;\n verified: boolean;\n visibility: 'public' | 'private';\n}\n\nexport interface GithubToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n scope: string;\n expires_in: number;\n refresh_token: string;\n}\n\ninterface GithubErrorResponse {\n error: string;\n error_description: string;\n error_uri: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA0B;AAE1B,mBAA4B;AAC5B,oBAAkE;AAE3D,SAAS,uBAAiC;AAC/C,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,cAAc,CAAC,YAAY;AAAA,IAC3B,uBAAuB,EAAE,MAAM,GAAG,GAAG,OAAO,GAAG;AAC7C,iBAAO,sCAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,GAAG,OAAO,GAAG;AACtD,YAAM,WAAW,UAAM,yCAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,yBAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,aAAa,GAAG;AAClC,kCAAU,cAAc,0BAA0B;AAClD,kCAAU,KAAK,aAAa,yBAAyB;AACrD,YAAM,UAAU,EAAE,QAAQ,oBAAoB,eAAe,UAAU,YAAY,GAAG;AACtF,YAAM,WAAW,MAAM,MAAM,KAAK,aAAa,EAAE,QAAQ,CAAC;AAC1D,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,yBAAY,SAAS,QAAQ,mBAAmB,2BAA2B;AAAA,MACvF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,YAAM,gBAAgB,MAAM,MAAM,sCAAsC,EAAE,QAAQ,CAAC;AACnF,UAAI,CAAC,cAAc,IAAI;AACrB,cAAM,IAAI;AAAA,UACR,cAAc;AAAA,UACd;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,YAAM,SAAU,MAAM,cAAc,KAAK;AACzC,WAAK,QAAQ,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,GAAG,SAAS,KAAK;AAClE,YAAM,iBAAiB,OAAO,KAAK,CAAC,UAAU,MAAM,UAAU,KAAK,KAAK,GAAG,YAAY;AACvF,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK,QAAQ,KAAK;AAAA,UACxB;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,YAAY,KAAK,aAAa,KAAK,MAAM,KAAK,UAAU,IAAI;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/github.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { OAuth2Token, Provider } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode } from './common';\n\nexport function createGithubProvider(): Provider {\n return {\n authorizationUri: 'https://github.com/login/oauth/authorize',\n tokenUri: 'https://github.com/login/oauth/access_token',\n userInfoUri: 'https://api.github.com/user',\n userNameAttribute: 'id',\n defaultScope: ['user:email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GithubErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GithubToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n const response = await fetch(this.userInfoUri, { headers });\n if (!response.ok) {\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to fetch user info');\n }\n const data = (await response.json()) as GithubUserInfo;\n const emailResponse = await fetch('https://api.github.com/user/emails', { headers });\n if (!emailResponse.ok) {\n throw new OAuth2Error(\n emailResponse.status,\n 'invalid_request',\n 'Failed to fetch user email'\n );\n }\n const emails = (await emailResponse.json()) as GithubEmail[];\n data.email = emails.find((email) => email.primary)?.email || data.email;\n const email_verified = emails.find((email) => email.email === data.email)?.verified || false;\n return {\n data,\n claims: {\n sub: data.id,\n name: data.name || data.login,\n email_verified,\n email: data.email,\n picture: data.avatar_url,\n profile: data.html_url,\n website: data.html_url,\n updated_at: data.updated_at ? Date.parse(data.updated_at) : undefined,\n },\n };\n },\n };\n}\n\nexport const github = createGithubProvider();\n\nexport interface GithubUserInfo {\n login: string;\n id: string;\n node_id: string;\n avatar_url: string;\n gravatar_id: string;\n url: string;\n html_url: string;\n followers_url: string;\n following_url: string;\n gists_url: string;\n starred_url: string;\n subscriptions_url: string;\n organizations_url: string;\n repos_url: string;\n events_url: string;\n received_events_url: string;\n type: string;\n site_admin: boolean;\n name: string;\n company: string;\n blog: string;\n location: string;\n email: string;\n hireable: boolean;\n bio: string;\n twitter_username: string;\n public_repos: string;\n public_gists: string;\n followers: string;\n following: string;\n created_at: string;\n updated_at: string;\n private_gists: string;\n total_private_repos: string;\n owned_private_repos: string;\n disk_usage: string;\n collaborators: string;\n two_factor_authentication: boolean;\n plan: {\n name: string;\n space: string;\n private_repos: string;\n collaborators: string;\n };\n}\n\ninterface GithubEmail {\n email: string;\n primary: boolean;\n verified: boolean;\n visibility: 'public' | 'private';\n}\n\nexport interface GithubToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n scope: string;\n expires_in: number;\n refresh_token: string;\n}\n\ninterface GithubErrorResponse {\n error: string;\n error_description: string;\n error_uri: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA0B;AAC1B,mBAA4B;AAE5B,oBAAkE;AAE3D,SAAS,uBAAiC;AAC/C,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,cAAc,CAAC,YAAY;AAAA,IAC3B,uBAAuB,EAAE,MAAM,GAAG,GAAG,OAAO,GAAG;AAC7C,iBAAO,sCAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,GAAG,OAAO,GAAG;AACtD,YAAM,WAAW,UAAM,yCAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,yBAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,aAAa,GAAG;AAClC,kCAAU,cAAc,0BAA0B;AAClD,kCAAU,KAAK,aAAa,yBAAyB;AACrD,YAAM,UAAU,EAAE,QAAQ,oBAAoB,eAAe,UAAU,YAAY,GAAG;AACtF,YAAM,WAAW,MAAM,MAAM,KAAK,aAAa,EAAE,QAAQ,CAAC;AAC1D,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,yBAAY,SAAS,QAAQ,mBAAmB,2BAA2B;AAAA,MACvF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,YAAM,gBAAgB,MAAM,MAAM,sCAAsC,EAAE,QAAQ,CAAC;AACnF,UAAI,CAAC,cAAc,IAAI;AACrB,cAAM,IAAI;AAAA,UACR,cAAc;AAAA,UACd;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,YAAM,SAAU,MAAM,cAAc,KAAK;AACzC,WAAK,QAAQ,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,GAAG,SAAS,KAAK;AAClE,YAAM,iBAAiB,OAAO,KAAK,CAAC,UAAU,MAAM,UAAU,KAAK,KAAK,GAAG,YAAY;AACvF,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK,QAAQ,KAAK;AAAA,UACxB;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,YAAY,KAAK,aAAa,KAAK,MAAM,KAAK,UAAU,IAAI;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/github.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport type { OAuth2Token, Provider } from '../types';\nimport { OAuth2Error } from '../error';\nimport { createAuthorizationUri, exchangeAuthorizationCode } from './common';\n\nexport function createGithubProvider(): Provider {\n return {\n authorizationUri: 'https://github.com/login/oauth/authorize',\n tokenUri: 'https://github.com/login/oauth/access_token',\n userInfoUri: 'https://api.github.com/user',\n userNameAttribute: 'id',\n defaultScope: ['user:email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GithubErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GithubToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n const response = await fetch(this.userInfoUri, { headers });\n if (!response.ok) {\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to fetch user info');\n }\n const data = (await response.json()) as GithubUserInfo;\n const emailResponse = await fetch('https://api.github.com/user/emails', { headers });\n if (!emailResponse.ok) {\n throw new OAuth2Error(\n emailResponse.status,\n 'invalid_request',\n 'Failed to fetch user email'\n );\n }\n const emails = (await emailResponse.json()) as GithubEmail[];\n data.email = emails.find((email) => email.primary)?.email || data.email;\n const email_verified = emails.find((email) => email.email === data.email)?.verified || false;\n return {\n data,\n claims: {\n sub: data.id,\n name: data.name || data.login,\n email_verified,\n email: data.email,\n picture: data.avatar_url,\n profile: data.html_url,\n website: data.html_url,\n updated_at: data.updated_at ? Date.parse(data.updated_at) : undefined,\n },\n };\n },\n };\n}\n\nexport const github = createGithubProvider();\n\nexport interface GithubUserInfo {\n login: string;\n id: string;\n node_id: string;\n avatar_url: string;\n gravatar_id: string;\n url: string;\n html_url: string;\n followers_url: string;\n following_url: string;\n gists_url: string;\n starred_url: string;\n subscriptions_url: string;\n organizations_url: string;\n repos_url: string;\n events_url: string;\n received_events_url: string;\n type: string;\n site_admin: boolean;\n name: string;\n company: string;\n blog: string;\n location: string;\n email: string;\n hireable: boolean;\n bio: string;\n twitter_username: string;\n public_repos: string;\n public_gists: string;\n followers: string;\n following: string;\n created_at: string;\n updated_at: string;\n private_gists: string;\n total_private_repos: string;\n owned_private_repos: string;\n disk_usage: string;\n collaborators: string;\n two_factor_authentication: boolean;\n plan: {\n name: string;\n space: string;\n private_repos: string;\n collaborators: string;\n };\n}\n\ninterface GithubEmail {\n email: string;\n primary: boolean;\n verified: boolean;\n visibility: 'public' | 'private';\n}\n\nexport interface GithubToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n scope: string;\n expires_in: number;\n refresh_token: string;\n}\n\ninterface GithubErrorResponse {\n error: string;\n error_description: string;\n error_uri: string;\n}\n"],"mappings":";AAAA,SAAS,iBAAiB;AAE1B,SAAS,mBAAmB;AAC5B,SAAS,wBAAwB,iCAAiC;AAE3D,SAAS,uBAAiC;AAC/C,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,cAAc,CAAC,YAAY;AAAA,IAC3B,uBAAuB,EAAE,MAAM,GAAG,GAAG,OAAO,GAAG;AAC7C,aAAO,uBAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,GAAG,OAAO,GAAG;AACtD,YAAM,WAAW,MAAM,0BAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,YAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,aAAa,GAAG;AAClC,gBAAU,cAAc,0BAA0B;AAClD,gBAAU,KAAK,aAAa,yBAAyB;AACrD,YAAM,UAAU,EAAE,QAAQ,oBAAoB,eAAe,UAAU,YAAY,GAAG;AACtF,YAAM,WAAW,MAAM,MAAM,KAAK,aAAa,EAAE,QAAQ,CAAC;AAC1D,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,YAAY,SAAS,QAAQ,mBAAmB,2BAA2B;AAAA,MACvF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,YAAM,gBAAgB,MAAM,MAAM,sCAAsC,EAAE,QAAQ,CAAC;AACnF,UAAI,CAAC,cAAc,IAAI;AACrB,cAAM,IAAI;AAAA,UACR,cAAc;AAAA,UACd;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,YAAM,SAAU,MAAM,cAAc,KAAK;AACzC,WAAK,QAAQ,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,GAAG,SAAS,KAAK;AAClE,YAAM,iBAAiB,OAAO,KAAK,CAAC,UAAU,MAAM,UAAU,KAAK,KAAK,GAAG,YAAY;AACvF,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK,QAAQ,KAAK;AAAA,UACxB;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,YAAY,KAAK,aAAa,KAAK,MAAM,KAAK,UAAU,IAAI;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/github.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { OAuth2Token, Provider } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode } from './common';\n\nexport function createGithubProvider(): Provider {\n return {\n authorizationUri: 'https://github.com/login/oauth/authorize',\n tokenUri: 'https://github.com/login/oauth/access_token',\n userInfoUri: 'https://api.github.com/user',\n userNameAttribute: 'id',\n defaultScope: ['user:email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GithubErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GithubToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n const response = await fetch(this.userInfoUri, { headers });\n if (!response.ok) {\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to fetch user info');\n }\n const data = (await response.json()) as GithubUserInfo;\n const emailResponse = await fetch('https://api.github.com/user/emails', { headers });\n if (!emailResponse.ok) {\n throw new OAuth2Error(\n emailResponse.status,\n 'invalid_request',\n 'Failed to fetch user email'\n );\n }\n const emails = (await emailResponse.json()) as GithubEmail[];\n data.email = emails.find((email) => email.primary)?.email || data.email;\n const email_verified = emails.find((email) => email.email === data.email)?.verified || false;\n return {\n data,\n claims: {\n sub: data.id,\n name: data.name || data.login,\n email_verified,\n email: data.email,\n picture: data.avatar_url,\n profile: data.html_url,\n website: data.html_url,\n updated_at: data.updated_at ? Date.parse(data.updated_at) : undefined,\n },\n };\n },\n };\n}\n\nexport const github = createGithubProvider();\n\nexport interface GithubUserInfo {\n login: string;\n id: string;\n node_id: string;\n avatar_url: string;\n gravatar_id: string;\n url: string;\n html_url: string;\n followers_url: string;\n following_url: string;\n gists_url: string;\n starred_url: string;\n subscriptions_url: string;\n organizations_url: string;\n repos_url: string;\n events_url: string;\n received_events_url: string;\n type: string;\n site_admin: boolean;\n name: string;\n company: string;\n blog: string;\n location: string;\n email: string;\n hireable: boolean;\n bio: string;\n twitter_username: string;\n public_repos: string;\n public_gists: string;\n followers: string;\n following: string;\n created_at: string;\n updated_at: string;\n private_gists: string;\n total_private_repos: string;\n owned_private_repos: string;\n disk_usage: string;\n collaborators: string;\n two_factor_authentication: boolean;\n plan: {\n name: string;\n space: string;\n private_repos: string;\n collaborators: string;\n };\n}\n\ninterface GithubEmail {\n email: string;\n primary: boolean;\n verified: boolean;\n visibility: 'public' | 'private';\n}\n\nexport interface GithubToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n scope: string;\n expires_in: number;\n refresh_token: string;\n}\n\ninterface GithubErrorResponse {\n error: string;\n error_description: string;\n error_uri: string;\n}\n"],"mappings":";AAAA,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAE5B,SAAS,wBAAwB,iCAAiC;AAE3D,SAAS,uBAAiC;AAC/C,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,cAAc,CAAC,YAAY;AAAA,IAC3B,uBAAuB,EAAE,MAAM,GAAG,GAAG,OAAO,GAAG;AAC7C,aAAO,uBAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,GAAG,OAAO,GAAG;AACtD,YAAM,WAAW,MAAM,0BAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,YAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,aAAa,GAAG;AAClC,gBAAU,cAAc,0BAA0B;AAClD,gBAAU,KAAK,aAAa,yBAAyB;AACrD,YAAM,UAAU,EAAE,QAAQ,oBAAoB,eAAe,UAAU,YAAY,GAAG;AACtF,YAAM,WAAW,MAAM,MAAM,KAAK,aAAa,EAAE,QAAQ,CAAC;AAC1D,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,YAAY,SAAS,QAAQ,mBAAmB,2BAA2B;AAAA,MACvF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,YAAM,gBAAgB,MAAM,MAAM,sCAAsC,EAAE,QAAQ,CAAC;AACnF,UAAI,CAAC,cAAc,IAAI;AACrB,cAAM,IAAI;AAAA,UACR,cAAc;AAAA,UACd;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,YAAM,SAAU,MAAM,cAAc,KAAK;AACzC,WAAK,QAAQ,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,GAAG,SAAS,KAAK;AAClE,YAAM,iBAAiB,OAAO,KAAK,CAAC,UAAU,MAAM,UAAU,KAAK,KAAK,GAAG,YAAY;AACvF,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK,QAAQ,KAAK;AAAA,UACxB;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,YAAY,KAAK,aAAa,KAAK,MAAM,KAAK,UAAU,IAAI;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/google.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport type {\n LoginOAuth2NativeParams,\n NativeCredential,\n OAuth2Token,\n OneTapProvider,\n} from '../types';\nimport { OAuth2Error } from '../error';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\nexport function createGoogleProvider(): OneTapProvider {\n return {\n authorizationUri: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUri: 'https://oauth2.googleapis.com/token',\n userInfoUri: 'https://www.googleapis.com/oauth2/v3/userinfo',\n userNameAttribute: 'sub',\n jwkSetUri: 'https://www.googleapis.com/oauth2/v3/certs',\n // https://developers.google.com/identity/protocols/oauth2/scopes#oauth2\n defaultScope: [\n 'https://www.googleapis.com/auth/userinfo.email',\n 'https://www.googleapis.com/auth/userinfo.profile',\n 'openid',\n ],\n createAuthorizationUri(params) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GoogleToken;\n },\n async getUserInfo({ id_token }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n };\n },\n async loginOAuth2Native({ pkce: _, credentials, ...params }: LoginOAuth2NativeParams) {\n invariant(credentials.code, 'code is required');\n const { code } = credentials;\n const { tokenUri } = this;\n const response = await exchangeAuthorizationCode({ code, tokenUri, ...params });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n const token = (await response.json()) as GoogleToken;\n const userInfo = await this.getUserInfo(token);\n return { token, userInfo };\n },\n async getTokenInfo(id_token: string) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n token: {\n id_token,\n access_token: '',\n token_type: '',\n expires_in: 0,\n refresh_token: '',\n scope: '',\n },\n userInfo: {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n },\n };\n },\n };\n}\n\nexport const google = createGoogleProvider();\n\nexport interface GoogleUserInfo {\n aud: string;\n azp: string;\n email: string;\n email_verified: boolean;\n exp: number;\n family_name: string;\n given_name: string;\n hd?: string;\n iat: number;\n iss: string;\n jti?: string;\n locale?: string;\n name: string;\n nbf?: number;\n picture: string;\n sub: string;\n nonce?: string;\n}\n\nexport interface GoogleToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n id_token: string;\n scope: string;\n}\n\n// android,ios has different client_id and redirect_uri\nexport interface GoogleAppCredential extends NativeCredential {\n state: string;\n code: string;\n client_id: string;\n redirect_uri: string;\n}\n\nexport interface GoogleErrorResponse {\n error:\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'invalid_token'\n | 'invalid_scope'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'access_denied'\n | 'server_error';\n error_description: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA0B;AAO1B,mBAA4B;AAC5B,oBAAiF;AAE1E,SAAS,uBAAuC;AACrD,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,WAAW;AAAA;AAAA,IAEX,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,uBAAuB,QAAQ;AAC7B,iBAAO,sCAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,QAAQ;AACtC,YAAM,WAAW,UAAM,yCAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,yBAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,SAAS,GAAG;AAC9B,kCAAU,UAAU,sBAAsB;AAC1C,kCAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,UAAM,6BAA8B,UAAU,KAAK,SAAS;AACzE,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,SAAS,KAAK;AAAA,UACd,OAAO,KAAK;AAAA,UACZ,gBAAgB,KAAK;AAAA,UACrB,YAAY,KAAK;AAAA,UACjB,aAAa,KAAK;AAAA,UAClB,QAAQ,KAAK;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,MAAM,GAAG,aAAa,GAAG,OAAO,GAA4B;AACpF,kCAAU,YAAY,MAAM,kBAAkB;AAC9C,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,EAAE,SAAS,IAAI;AACrB,YAAM,WAAW,UAAM,yCAA0B,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC;AAC9E,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,yBAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,YAAM,QAAS,MAAM,SAAS,KAAK;AACnC,YAAM,WAAW,MAAM,KAAK,YAAY,KAAK;AAC7C,aAAO,EAAE,OAAO,SAAS;AAAA,IAC3B;AAAA,IACA,MAAM,aAAa,UAAkB;AACnC,kCAAU,UAAU,sBAAsB;AAC1C,kCAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,UAAM,6BAA8B,UAAU,KAAK,SAAS;AACzE,aAAO;AAAA,QACL,OAAO;AAAA,UACL;AAAA,UACA,cAAc;AAAA,UACd,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,OAAO;AAAA,QACT;AAAA,QACA,UAAU;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,YACN,KAAK,KAAK;AAAA,YACV,MAAM,KAAK;AAAA,YACX,SAAS,KAAK;AAAA,YACd,OAAO,KAAK;AAAA,YACZ,gBAAgB,KAAK;AAAA,YACrB,YAAY,KAAK;AAAA,YACjB,aAAa,KAAK;AAAA,YAClB,QAAQ,KAAK;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/google.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type {\n LoginOAuth2NativeParams,\n NativeCredential,\n OAuth2Token,\n OneTapProvider,\n} from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\nexport function createGoogleProvider(): OneTapProvider {\n return {\n authorizationUri: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUri: 'https://oauth2.googleapis.com/token',\n userInfoUri: 'https://www.googleapis.com/oauth2/v3/userinfo',\n userNameAttribute: 'sub',\n jwkSetUri: 'https://www.googleapis.com/oauth2/v3/certs',\n // https://developers.google.com/identity/protocols/oauth2/scopes#oauth2\n defaultScope: [\n 'https://www.googleapis.com/auth/userinfo.email',\n 'https://www.googleapis.com/auth/userinfo.profile',\n 'openid',\n ],\n createAuthorizationUri(params) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GoogleToken;\n },\n async getUserInfo({ id_token }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n };\n },\n async loginOAuth2Native({ pkce: _, credentials, ...params }: LoginOAuth2NativeParams) {\n invariant(credentials.code, 'code is required');\n const { code } = credentials;\n const { tokenUri } = this;\n const response = await exchangeAuthorizationCode({ code, tokenUri, ...params });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n const token = (await response.json()) as GoogleToken;\n const userInfo = await this.getUserInfo(token);\n return { token, userInfo };\n },\n async getTokenInfo(id_token: string) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n token: {\n id_token,\n access_token: '',\n token_type: '',\n expires_in: 0,\n refresh_token: '',\n scope: '',\n },\n userInfo: {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n },\n };\n },\n };\n}\n\nexport const google = createGoogleProvider();\n\nexport interface GoogleUserInfo {\n aud: string;\n azp: string;\n email: string;\n email_verified: boolean;\n exp: number;\n family_name: string;\n given_name: string;\n hd?: string;\n iat: number;\n iss: string;\n jti?: string;\n locale?: string;\n name: string;\n nbf?: number;\n picture: string;\n sub: string;\n nonce?: string;\n}\n\nexport interface GoogleToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n id_token: string;\n scope: string;\n}\n\n// android,ios has different client_id and redirect_uri\nexport interface GoogleAppCredential extends NativeCredential {\n state: string;\n code: string;\n client_id: string;\n redirect_uri: string;\n}\n\nexport interface GoogleErrorResponse {\n error:\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'invalid_token'\n | 'invalid_scope'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'access_denied'\n | 'server_error';\n error_description: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA0B;AAC1B,mBAA4B;AAO5B,oBAAiF;AAE1E,SAAS,uBAAuC;AACrD,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,WAAW;AAAA;AAAA,IAEX,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,uBAAuB,QAAQ;AAC7B,iBAAO,sCAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,QAAQ;AACtC,YAAM,WAAW,UAAM,yCAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,yBAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,SAAS,GAAG;AAC9B,kCAAU,UAAU,sBAAsB;AAC1C,kCAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,UAAM,6BAA8B,UAAU,KAAK,SAAS;AACzE,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,SAAS,KAAK;AAAA,UACd,OAAO,KAAK;AAAA,UACZ,gBAAgB,KAAK;AAAA,UACrB,YAAY,KAAK;AAAA,UACjB,aAAa,KAAK;AAAA,UAClB,QAAQ,KAAK;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,MAAM,GAAG,aAAa,GAAG,OAAO,GAA4B;AACpF,kCAAU,YAAY,MAAM,kBAAkB;AAC9C,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,EAAE,SAAS,IAAI;AACrB,YAAM,WAAW,UAAM,yCAA0B,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC;AAC9E,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,yBAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,YAAM,QAAS,MAAM,SAAS,KAAK;AACnC,YAAM,WAAW,MAAM,KAAK,YAAY,KAAK;AAC7C,aAAO,EAAE,OAAO,SAAS;AAAA,IAC3B;AAAA,IACA,MAAM,aAAa,UAAkB;AACnC,kCAAU,UAAU,sBAAsB;AAC1C,kCAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,UAAM,6BAA8B,UAAU,KAAK,SAAS;AACzE,aAAO;AAAA,QACL,OAAO;AAAA,UACL;AAAA,UACA,cAAc;AAAA,UACd,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,OAAO;AAAA,QACT;AAAA,QACA,UAAU;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,YACN,KAAK,KAAK;AAAA,YACV,MAAM,KAAK;AAAA,YACX,SAAS,KAAK;AAAA,YACd,OAAO,KAAK;AAAA,YACZ,gBAAgB,KAAK;AAAA,YACrB,YAAY,KAAK;AAAA,YACjB,aAAa,KAAK;AAAA,YAClB,QAAQ,KAAK;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/google.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport type {\n LoginOAuth2NativeParams,\n NativeCredential,\n OAuth2Token,\n OneTapProvider,\n} from '../types';\nimport { OAuth2Error } from '../error';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\nexport function createGoogleProvider(): OneTapProvider {\n return {\n authorizationUri: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUri: 'https://oauth2.googleapis.com/token',\n userInfoUri: 'https://www.googleapis.com/oauth2/v3/userinfo',\n userNameAttribute: 'sub',\n jwkSetUri: 'https://www.googleapis.com/oauth2/v3/certs',\n // https://developers.google.com/identity/protocols/oauth2/scopes#oauth2\n defaultScope: [\n 'https://www.googleapis.com/auth/userinfo.email',\n 'https://www.googleapis.com/auth/userinfo.profile',\n 'openid',\n ],\n createAuthorizationUri(params) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GoogleToken;\n },\n async getUserInfo({ id_token }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n };\n },\n async loginOAuth2Native({ pkce: _, credentials, ...params }: LoginOAuth2NativeParams) {\n invariant(credentials.code, 'code is required');\n const { code } = credentials;\n const { tokenUri } = this;\n const response = await exchangeAuthorizationCode({ code, tokenUri, ...params });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n const token = (await response.json()) as GoogleToken;\n const userInfo = await this.getUserInfo(token);\n return { token, userInfo };\n },\n async getTokenInfo(id_token: string) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n token: {\n id_token,\n access_token: '',\n token_type: '',\n expires_in: 0,\n refresh_token: '',\n scope: '',\n },\n userInfo: {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n },\n };\n },\n };\n}\n\nexport const google = createGoogleProvider();\n\nexport interface GoogleUserInfo {\n aud: string;\n azp: string;\n email: string;\n email_verified: boolean;\n exp: number;\n family_name: string;\n given_name: string;\n hd?: string;\n iat: number;\n iss: string;\n jti?: string;\n locale?: string;\n name: string;\n nbf?: number;\n picture: string;\n sub: string;\n nonce?: string;\n}\n\nexport interface GoogleToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n id_token: string;\n scope: string;\n}\n\n// android,ios has different client_id and redirect_uri\nexport interface GoogleAppCredential extends NativeCredential {\n state: string;\n code: string;\n client_id: string;\n redirect_uri: string;\n}\n\nexport interface GoogleErrorResponse {\n error:\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'invalid_token'\n | 'invalid_scope'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'access_denied'\n | 'server_error';\n error_description: string;\n}\n"],"mappings":";AAAA,SAAS,iBAAiB;AAO1B,SAAS,mBAAmB;AAC5B,SAAS,wBAAwB,2BAA2B,qBAAqB;AAE1E,SAAS,uBAAuC;AACrD,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,WAAW;AAAA;AAAA,IAEX,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,uBAAuB,QAAQ;AAC7B,aAAO,uBAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,QAAQ;AACtC,YAAM,WAAW,MAAM,0BAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,YAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,SAAS,GAAG;AAC9B,gBAAU,UAAU,sBAAsB;AAC1C,gBAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,MAAM,cAA8B,UAAU,KAAK,SAAS;AACzE,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,SAAS,KAAK;AAAA,UACd,OAAO,KAAK;AAAA,UACZ,gBAAgB,KAAK;AAAA,UACrB,YAAY,KAAK;AAAA,UACjB,aAAa,KAAK;AAAA,UAClB,QAAQ,KAAK;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,MAAM,GAAG,aAAa,GAAG,OAAO,GAA4B;AACpF,gBAAU,YAAY,MAAM,kBAAkB;AAC9C,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,EAAE,SAAS,IAAI;AACrB,YAAM,WAAW,MAAM,0BAA0B,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC;AAC9E,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,YAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,YAAM,QAAS,MAAM,SAAS,KAAK;AACnC,YAAM,WAAW,MAAM,KAAK,YAAY,KAAK;AAC7C,aAAO,EAAE,OAAO,SAAS;AAAA,IAC3B;AAAA,IACA,MAAM,aAAa,UAAkB;AACnC,gBAAU,UAAU,sBAAsB;AAC1C,gBAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,MAAM,cAA8B,UAAU,KAAK,SAAS;AACzE,aAAO;AAAA,QACL,OAAO;AAAA,UACL;AAAA,UACA,cAAc;AAAA,UACd,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,OAAO;AAAA,QACT;AAAA,QACA,UAAU;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,YACN,KAAK,KAAK;AAAA,YACV,MAAM,KAAK;AAAA,YACX,SAAS,KAAK;AAAA,YACd,OAAO,KAAK;AAAA,YACZ,gBAAgB,KAAK;AAAA,YACrB,YAAY,KAAK;AAAA,YACjB,aAAa,KAAK;AAAA,YAClB,QAAQ,KAAK;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/google.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type {\n LoginOAuth2NativeParams,\n NativeCredential,\n OAuth2Token,\n OneTapProvider,\n} from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\nexport function createGoogleProvider(): OneTapProvider {\n return {\n authorizationUri: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUri: 'https://oauth2.googleapis.com/token',\n userInfoUri: 'https://www.googleapis.com/oauth2/v3/userinfo',\n userNameAttribute: 'sub',\n jwkSetUri: 'https://www.googleapis.com/oauth2/v3/certs',\n // https://developers.google.com/identity/protocols/oauth2/scopes#oauth2\n defaultScope: [\n 'https://www.googleapis.com/auth/userinfo.email',\n 'https://www.googleapis.com/auth/userinfo.profile',\n 'openid',\n ],\n createAuthorizationUri(params) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GoogleToken;\n },\n async getUserInfo({ id_token }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n };\n },\n async loginOAuth2Native({ pkce: _, credentials, ...params }: LoginOAuth2NativeParams) {\n invariant(credentials.code, 'code is required');\n const { code } = credentials;\n const { tokenUri } = this;\n const response = await exchangeAuthorizationCode({ code, tokenUri, ...params });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n const token = (await response.json()) as GoogleToken;\n const userInfo = await this.getUserInfo(token);\n return { token, userInfo };\n },\n async getTokenInfo(id_token: string) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n token: {\n id_token,\n access_token: '',\n token_type: '',\n expires_in: 0,\n refresh_token: '',\n scope: '',\n },\n userInfo: {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n },\n };\n },\n };\n}\n\nexport const google = createGoogleProvider();\n\nexport interface GoogleUserInfo {\n aud: string;\n azp: string;\n email: string;\n email_verified: boolean;\n exp: number;\n family_name: string;\n given_name: string;\n hd?: string;\n iat: number;\n iss: string;\n jti?: string;\n locale?: string;\n name: string;\n nbf?: number;\n picture: string;\n sub: string;\n nonce?: string;\n}\n\nexport interface GoogleToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n id_token: string;\n scope: string;\n}\n\n// android,ios has different client_id and redirect_uri\nexport interface GoogleAppCredential extends NativeCredential {\n state: string;\n code: string;\n client_id: string;\n redirect_uri: string;\n}\n\nexport interface GoogleErrorResponse {\n error:\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'invalid_token'\n | 'invalid_scope'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'access_denied'\n | 'server_error';\n error_description: string;\n}\n"],"mappings":";AAAA,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAO5B,SAAS,wBAAwB,2BAA2B,qBAAqB;AAE1E,SAAS,uBAAuC;AACrD,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,WAAW;AAAA;AAAA,IAEX,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,uBAAuB,QAAQ;AAC7B,aAAO,uBAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,QAAQ;AACtC,YAAM,WAAW,MAAM,0BAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,YAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,SAAS,GAAG;AAC9B,gBAAU,UAAU,sBAAsB;AAC1C,gBAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,MAAM,cAA8B,UAAU,KAAK,SAAS;AACzE,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,SAAS,KAAK;AAAA,UACd,OAAO,KAAK;AAAA,UACZ,gBAAgB,KAAK;AAAA,UACrB,YAAY,KAAK;AAAA,UACjB,aAAa,KAAK;AAAA,UAClB,QAAQ,KAAK;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,MAAM,GAAG,aAAa,GAAG,OAAO,GAA4B;AACpF,gBAAU,YAAY,MAAM,kBAAkB;AAC9C,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,EAAE,SAAS,IAAI;AACrB,YAAM,WAAW,MAAM,0BAA0B,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC;AAC9E,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,YAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,YAAM,QAAS,MAAM,SAAS,KAAK;AACnC,YAAM,WAAW,MAAM,KAAK,YAAY,KAAK;AAC7C,aAAO,EAAE,OAAO,SAAS;AAAA,IAC3B;AAAA,IACA,MAAM,aAAa,UAAkB;AACnC,gBAAU,UAAU,sBAAsB;AAC1C,gBAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,MAAM,cAA8B,UAAU,KAAK,SAAS;AACzE,aAAO;AAAA,QACL,OAAO;AAAA,UACL;AAAA,UACA,cAAc;AAAA,UACd,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,OAAO;AAAA,QACT;AAAA,QACA,UAAU;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,YACN,KAAK,KAAK;AAAA,YACV,MAAM,KAAK;AAAA,YACX,SAAS,KAAK;AAAA,YACd,OAAO,KAAK;AAAA,YACZ,gBAAgB,KAAK;AAAA,YACrB,YAAY,KAAK;AAAA,YACjB,aAAa,KAAK;AAAA,YAClB,QAAQ,KAAK;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/wechat.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport type { OAuth2Token, Provider } from '../types';\nimport { OAuth2Error } from '../error';\n\nexport type Options = { isWechatBrowser: boolean };\n\n/**\n * reference: https://developers.weixin.qq.com/doc/offiaccount/en/OA_Web_Apps/Wechat_webpage_authorization.html\n */\nexport function createWechatProvider(options: Options = { isWechatBrowser: false }): Provider {\n const { isWechatBrowser } = options;\n return {\n authorizationUri: isWechatBrowser\n ? 'https://open.weixin.qq.com/connect/oauth2/authorize'\n : 'https://open.weixin.qq.com/connect/qrconnect',\n tokenUri: 'https://api.weixin.qq.com/sns/oauth2/access_token',\n tokenRefreshUri: 'https://api.weixin.qq.com/sns/oauth2/refresh_token',\n userInfoUri: 'https://api.weixin.qq.com/sns/userinfo',\n defaultScope: [isWechatBrowser ? 'snsapi_userinfo' : 'snsapi_login'],\n createAuthorizationUri(params) {\n const url = new URL(this.authorizationUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('scope', (params.scope ?? this.defaultScope).join(','));\n url.searchParams.set('redirect_uri', params.redirectUri);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('state', params.state);\n return url;\n },\n async exchangeAuthorizationCode(params) {\n const url = new URL(this.tokenUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('secret', params.clientSecret);\n url.searchParams.set('code', params.code);\n url.searchParams.set('grant_type', 'authorization_code');\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n async getUserInfo({ access_token, openid }) {\n invariant(this.userInfoUri, 'userInfoUri is required');\n invariant(openid && typeof openid === 'string', 'openid is required');\n const url = new URL(this.userInfoUri);\n url.searchParams.set('access_token', access_token);\n url.searchParams.set('openid', openid);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n const data = (await response.json()) as WechatUserInfo;\n\n return {\n data,\n claims: {\n sub: data.unionid ?? data.openid,\n name: data.nickname,\n picture: `${data.headimgurl.split('/').slice(0, -1).join('/')}/0`,\n gender: data.sex === 1 ? 'male' : data.sex === 2 ? 'female' : undefined,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const url = new URL(this.tokenRefreshUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('grant_type', 'refresh_token');\n url.searchParams.set('refresh_token', params.refreshToken);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n };\n}\n\nexport const wechat = createWechatProvider();\n\nexport interface WechatUserInfo {\n openid: string;\n nickname: string;\n sex: 0 | 1 | 2; // 0: unknown, 1: male, 2: female\n province: string;\n city: string;\n country: string;\n /**\n * User's profile photo. The last numeric value represents the size of a square profile photo\n * (The value can be 0, 46, 64, 96, or 132. The value 0 represents a 640*640 square profile\n * photo). This parameter is left blank if a user has no profile photo. If the user changes the\n * profile photo, the URL of the original profile photo will expire.\n * e.g. http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46\n */\n headimgurl: string;\n privilege: string[];\n unionid?: string;\n}\n\nexport interface WechatToken extends OAuth2Token {\n access_token: string;\n expires_in: number;\n refresh_token: string;\n openid: string;\n scope: string;\n unionid?: string;\n}\n\ninterface WechatErrorResponse {\n errcode: number;\n errmsg: string;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/wechat.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { OAuth2Token, Provider } from '../types';\n\nexport type Options = { isWechatBrowser: boolean };\n\n/**\n * reference: https://developers.weixin.qq.com/doc/offiaccount/en/OA_Web_Apps/Wechat_webpage_authorization.html\n */\nexport function createWechatProvider(options: Options = { isWechatBrowser: false }): Provider {\n const { isWechatBrowser } = options;\n return {\n authorizationUri: isWechatBrowser\n ? 'https://open.weixin.qq.com/connect/oauth2/authorize'\n : 'https://open.weixin.qq.com/connect/qrconnect',\n tokenUri: 'https://api.weixin.qq.com/sns/oauth2/access_token',\n tokenRefreshUri: 'https://api.weixin.qq.com/sns/oauth2/refresh_token',\n userInfoUri: 'https://api.weixin.qq.com/sns/userinfo',\n defaultScope: [isWechatBrowser ? 'snsapi_userinfo' : 'snsapi_login'],\n createAuthorizationUri(params) {\n const url = new URL(this.authorizationUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('scope', (params.scope ?? this.defaultScope).join(','));\n url.searchParams.set('redirect_uri', params.redirectUri);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('state', params.state);\n return url;\n },\n async exchangeAuthorizationCode(params) {\n const url = new URL(this.tokenUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('secret', params.clientSecret);\n url.searchParams.set('code', params.code);\n url.searchParams.set('grant_type', 'authorization_code');\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n async getUserInfo({ access_token, openid }) {\n invariant(this.userInfoUri, 'userInfoUri is required');\n invariant(openid && typeof openid === 'string', 'openid is required');\n const url = new URL(this.userInfoUri);\n url.searchParams.set('access_token', access_token);\n url.searchParams.set('openid', openid);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n const data = (await response.json()) as WechatUserInfo;\n\n return {\n data,\n claims: {\n sub: data.unionid ?? data.openid,\n name: data.nickname,\n picture: `${data.headimgurl.split('/').slice(0, -1).join('/')}/0`,\n gender: data.sex === 1 ? 'male' : data.sex === 2 ? 'female' : undefined,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const url = new URL(this.tokenRefreshUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('grant_type', 'refresh_token');\n url.searchParams.set('refresh_token', params.refreshToken);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n };\n}\n\nexport const wechat = createWechatProvider();\n\nexport interface WechatUserInfo {\n openid: string;\n nickname: string;\n sex: 0 | 1 | 2; // 0: unknown, 1: male, 2: female\n province: string;\n city: string;\n country: string;\n /**\n * User's profile photo. The last numeric value represents the size of a square profile photo\n * (The value can be 0, 46, 64, 96, or 132. The value 0 represents a 640*640 square profile\n * photo). This parameter is left blank if a user has no profile photo. If the user changes the\n * profile photo, the URL of the original profile photo will expire.\n * e.g. http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46\n */\n headimgurl: string;\n privilege: string[];\n unionid?: string;\n}\n\nexport interface WechatToken extends OAuth2Token {\n access_token: string;\n expires_in: number;\n refresh_token: string;\n openid: string;\n scope: string;\n unionid?: string;\n}\n\ninterface WechatErrorResponse {\n errcode: number;\n errmsg: string;\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/wechat.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport type { OAuth2Token, Provider } from '../types';\nimport { OAuth2Error } from '../error';\n\nexport type Options = { isWechatBrowser: boolean };\n\n/**\n * reference: https://developers.weixin.qq.com/doc/offiaccount/en/OA_Web_Apps/Wechat_webpage_authorization.html\n */\nexport function createWechatProvider(options: Options = { isWechatBrowser: false }): Provider {\n const { isWechatBrowser } = options;\n return {\n authorizationUri: isWechatBrowser\n ? 'https://open.weixin.qq.com/connect/oauth2/authorize'\n : 'https://open.weixin.qq.com/connect/qrconnect',\n tokenUri: 'https://api.weixin.qq.com/sns/oauth2/access_token',\n tokenRefreshUri: 'https://api.weixin.qq.com/sns/oauth2/refresh_token',\n userInfoUri: 'https://api.weixin.qq.com/sns/userinfo',\n defaultScope: [isWechatBrowser ? 'snsapi_userinfo' : 'snsapi_login'],\n createAuthorizationUri(params) {\n const url = new URL(this.authorizationUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('scope', (params.scope ?? this.defaultScope).join(','));\n url.searchParams.set('redirect_uri', params.redirectUri);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('state', params.state);\n return url;\n },\n async exchangeAuthorizationCode(params) {\n const url = new URL(this.tokenUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('secret', params.clientSecret);\n url.searchParams.set('code', params.code);\n url.searchParams.set('grant_type', 'authorization_code');\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n async getUserInfo({ access_token, openid }) {\n invariant(this.userInfoUri, 'userInfoUri is required');\n invariant(openid && typeof openid === 'string', 'openid is required');\n const url = new URL(this.userInfoUri);\n url.searchParams.set('access_token', access_token);\n url.searchParams.set('openid', openid);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n const data = (await response.json()) as WechatUserInfo;\n\n return {\n data,\n claims: {\n sub: data.unionid ?? data.openid,\n name: data.nickname,\n picture: `${data.headimgurl.split('/').slice(0, -1).join('/')}/0`,\n gender: data.sex === 1 ? 'male' : data.sex === 2 ? 'female' : undefined,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const url = new URL(this.tokenRefreshUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('grant_type', 'refresh_token');\n url.searchParams.set('refresh_token', params.refreshToken);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n };\n}\n\nexport const wechat = createWechatProvider();\n\nexport interface WechatUserInfo {\n openid: string;\n nickname: string;\n sex: 0 | 1 | 2; // 0: unknown, 1: male, 2: female\n province: string;\n city: string;\n country: string;\n /**\n * User's profile photo. The last numeric value represents the size of a square profile photo\n * (The value can be 0, 46, 64, 96, or 132. The value 0 represents a 640*640 square profile\n * photo). This parameter is left blank if a user has no profile photo. If the user changes the\n * profile photo, the URL of the original profile photo will expire.\n * e.g. http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46\n */\n headimgurl: string;\n privilege: string[];\n unionid?: string;\n}\n\nexport interface WechatToken extends OAuth2Token {\n access_token: string;\n expires_in: number;\n refresh_token: string;\n openid: string;\n scope: string;\n unionid?: string;\n}\n\ninterface WechatErrorResponse {\n errcode: number;\n errmsg: string;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/wechat.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { OAuth2Token, Provider } from '../types';\n\nexport type Options = { isWechatBrowser: boolean };\n\n/**\n * reference: https://developers.weixin.qq.com/doc/offiaccount/en/OA_Web_Apps/Wechat_webpage_authorization.html\n */\nexport function createWechatProvider(options: Options = { isWechatBrowser: false }): Provider {\n const { isWechatBrowser } = options;\n return {\n authorizationUri: isWechatBrowser\n ? 'https://open.weixin.qq.com/connect/oauth2/authorize'\n : 'https://open.weixin.qq.com/connect/qrconnect',\n tokenUri: 'https://api.weixin.qq.com/sns/oauth2/access_token',\n tokenRefreshUri: 'https://api.weixin.qq.com/sns/oauth2/refresh_token',\n userInfoUri: 'https://api.weixin.qq.com/sns/userinfo',\n defaultScope: [isWechatBrowser ? 'snsapi_userinfo' : 'snsapi_login'],\n createAuthorizationUri(params) {\n const url = new URL(this.authorizationUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('scope', (params.scope ?? this.defaultScope).join(','));\n url.searchParams.set('redirect_uri', params.redirectUri);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('state', params.state);\n return url;\n },\n async exchangeAuthorizationCode(params) {\n const url = new URL(this.tokenUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('secret', params.clientSecret);\n url.searchParams.set('code', params.code);\n url.searchParams.set('grant_type', 'authorization_code');\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n async getUserInfo({ access_token, openid }) {\n invariant(this.userInfoUri, 'userInfoUri is required');\n invariant(openid && typeof openid === 'string', 'openid is required');\n const url = new URL(this.userInfoUri);\n url.searchParams.set('access_token', access_token);\n url.searchParams.set('openid', openid);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n const data = (await response.json()) as WechatUserInfo;\n\n return {\n data,\n claims: {\n sub: data.unionid ?? data.openid,\n name: data.nickname,\n picture: `${data.headimgurl.split('/').slice(0, -1).join('/')}/0`,\n gender: data.sex === 1 ? 'male' : data.sex === 2 ? 'female' : undefined,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const url = new URL(this.tokenRefreshUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('grant_type', 'refresh_token');\n url.searchParams.set('refresh_token', params.refreshToken);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n };\n}\n\nexport const wechat = createWechatProvider();\n\nexport interface WechatUserInfo {\n openid: string;\n nickname: string;\n sex: 0 | 1 | 2; // 0: unknown, 1: male, 2: female\n province: string;\n city: string;\n country: string;\n /**\n * User's profile photo. The last numeric value represents the size of a square profile photo\n * (The value can be 0, 46, 64, 96, or 132. The value 0 represents a 640*640 square profile\n * photo). This parameter is left blank if a user has no profile photo. If the user changes the\n * profile photo, the URL of the original profile photo will expire.\n * e.g. http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46\n */\n headimgurl: string;\n privilege: string[];\n unionid?: string;\n}\n\nexport interface WechatToken extends OAuth2Token {\n access_token: string;\n expires_in: number;\n refresh_token: string;\n openid: string;\n scope: string;\n unionid?: string;\n}\n\ninterface WechatErrorResponse {\n errcode: number;\n errmsg: string;\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/x.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport type { OAuth2Token, Provider } from '../types';\nimport { OAuth2Error } from '../error';\nimport {\n createAuthorizationUri,\n exchangeAuthorizationCode,\n refreshAccessToken,\n revokeToken,\n} from './common';\n\nexport interface XOptions {\n userFields?: UserField[];\n}\n\nconst defaultUserFields = [\n 'id',\n 'name',\n 'profile_image_url',\n 'url',\n 'username',\n 'location',\n 'verified',\n];\n\nexport function createXProvider(options?: XOptions): Provider {\n return {\n authorizationUri: 'https://x.com/i/oauth2/authorize',\n tokenUri: 'https://api.x.com/2/oauth2/token',\n tokenRefreshUri: 'https://api.x.com/2/oauth2/token',\n tokenRevokeUri: 'https://api.x.com/2/oauth2/revoke',\n // https://docs.x.com/x-api/users/user-lookup-me\n userInfoUri: 'https://api.x.com/2/users/me',\n defaultScope: ['users.read', 'tweet.read', 'offline.access'],\n createAuthorizationUri(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n const response = await exchangeAuthorizationCode({\n ...params,\n tokenUri: this.tokenUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n\n const userFields = options?.userFields ?? defaultUserFields;\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n\n const url = new URL(this.userInfoUri);\n userFields.forEach((field) => url.searchParams.append('user.fields', field));\n\n const response = await fetch(this.userInfoUri, { headers });\n if (!response.ok) {\n const { errors } = (await response.json()) as XAPIErrorResponse;\n throw new OAuth2Error(\n response.status,\n 'invalid_request',\n `Failed to fetch user info: ${errors.at(0)?.detail ?? errors.at(0)?.title}`\n );\n }\n const profile = (await response.json()) as XUserInfo;\n return {\n data: profile,\n claims: {\n sub: profile.data.id,\n name: profile.data.name,\n picture: profile.data.profile_image_url,\n email: profile.data.email,\n email_verified: profile.data.verified || false,\n preferred_username: profile.data.username,\n profile: `https://x.com/${profile.data.username}`,\n website: profile.data.url,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const response = await refreshAccessToken({\n ...params,\n tokenUri: this.tokenRefreshUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async revokeToken(params) {\n invariant(this.tokenRevokeUri, 'tokenRevokeUri is required');\n return revokeToken({\n ...params,\n tokenRevokeUri: this.tokenRevokeUri,\n authentication: 'basic',\n });\n },\n };\n}\n\nexport const x = createXProvider();\n\ntype UserField =\n | 'affiliation'\n | 'connection_status'\n | 'created_at'\n | 'description'\n | 'entities'\n | 'id'\n | 'is_identity_verified'\n | 'location'\n | 'most_recent_tweet_id'\n | 'name'\n | 'parody'\n | 'pinned_tweet_id'\n | 'profile_banner_url'\n | 'profile_image_url'\n | 'protected'\n | 'public_metrics'\n | 'receives_your_dm'\n | 'subscription'\n | 'subscription_type'\n | 'url'\n | 'username'\n | 'verified'\n | 'verified_followers_count'\n | 'verified_type'\n | 'withheld';\n\nexport interface XUser {\n id: string;\n name: string;\n email?: string;\n username: string;\n location?: string;\n entities?: {\n url: { urls: UrlEntity[] };\n description: {\n hashtags: HashtagEntity[];\n cashtags: HashtagEntity[];\n mentions: MentionEntity[];\n };\n };\n verified?: boolean;\n description?: string;\n url?: string;\n profile_image_url?: string;\n protected?: boolean;\n pinned_tweet_id?: string;\n created_at?: string;\n}\n\nexport interface XUserInfo {\n data: XUser;\n includes?: { tweets?: { id: string; text: string }[]; users?: XUser[] };\n}\n\ntype UrlEntity = {\n start: number;\n end: number;\n url: string;\n display_url: string;\n expanded_url: string;\n};\ntype HashtagEntity = { start: number; end: number; tag: string };\ntype MentionEntity = { start: number; end: string; username: string };\n\nexport interface XToken extends OAuth2Token {\n token_type: string;\n expires_in: number;\n access_token: string;\n scope: string;\n refresh_token: string;\n}\n\ntype XErrorResponse = { error: string; error_description: string };\ntype XAPIErrorResponse = {\n errors: { title: string; type: string; detail?: string; status?: number }[];\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/x.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { OAuth2Token, Provider } from '../types';\nimport {\n createAuthorizationUri,\n exchangeAuthorizationCode,\n refreshAccessToken,\n revokeToken,\n} from './common';\n\nexport interface XOptions {\n userFields?: UserField[];\n}\n\nconst defaultUserFields = [\n 'id',\n 'name',\n 'profile_image_url',\n 'url',\n 'username',\n 'location',\n 'verified',\n];\n\nexport function createXProvider(options?: XOptions): Provider {\n return {\n authorizationUri: 'https://x.com/i/oauth2/authorize',\n tokenUri: 'https://api.x.com/2/oauth2/token',\n tokenRefreshUri: 'https://api.x.com/2/oauth2/token',\n tokenRevokeUri: 'https://api.x.com/2/oauth2/revoke',\n // https://docs.x.com/x-api/users/user-lookup-me\n userInfoUri: 'https://api.x.com/2/users/me',\n defaultScope: ['users.read', 'tweet.read', 'offline.access'],\n createAuthorizationUri(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n const response = await exchangeAuthorizationCode({\n ...params,\n tokenUri: this.tokenUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n\n const userFields = options?.userFields ?? defaultUserFields;\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n\n const url = new URL(this.userInfoUri);\n userFields.forEach((field) => url.searchParams.append('user.fields', field));\n\n const response = await fetch(this.userInfoUri, { headers });\n if (!response.ok) {\n const { errors } = (await response.json()) as XAPIErrorResponse;\n throw new OAuth2Error(\n response.status,\n 'invalid_request',\n `Failed to fetch user info: ${errors.at(0)?.detail ?? errors.at(0)?.title}`\n );\n }\n const profile = (await response.json()) as XUserInfo;\n return {\n data: profile,\n claims: {\n sub: profile.data.id,\n name: profile.data.name,\n picture: profile.data.profile_image_url,\n email: profile.data.email,\n email_verified: profile.data.verified || false,\n preferred_username: profile.data.username,\n profile: `https://x.com/${profile.data.username}`,\n website: profile.data.url,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const response = await refreshAccessToken({\n ...params,\n tokenUri: this.tokenRefreshUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async revokeToken(params) {\n invariant(this.tokenRevokeUri, 'tokenRevokeUri is required');\n return revokeToken({\n ...params,\n tokenRevokeUri: this.tokenRevokeUri,\n authentication: 'basic',\n });\n },\n };\n}\n\nexport const x = createXProvider();\n\ntype UserField =\n | 'affiliation'\n | 'connection_status'\n | 'created_at'\n | 'description'\n | 'entities'\n | 'id'\n | 'is_identity_verified'\n | 'location'\n | 'most_recent_tweet_id'\n | 'name'\n | 'parody'\n | 'pinned_tweet_id'\n | 'profile_banner_url'\n | 'profile_image_url'\n | 'protected'\n | 'public_metrics'\n | 'receives_your_dm'\n | 'subscription'\n | 'subscription_type'\n | 'url'\n | 'username'\n | 'verified'\n | 'verified_followers_count'\n | 'verified_type'\n | 'withheld';\n\nexport interface XUser {\n id: string;\n name: string;\n email?: string;\n username: string;\n location?: string;\n entities?: {\n url: { urls: UrlEntity[] };\n description: {\n hashtags: HashtagEntity[];\n cashtags: HashtagEntity[];\n mentions: MentionEntity[];\n };\n };\n verified?: boolean;\n description?: string;\n url?: string;\n profile_image_url?: string;\n protected?: boolean;\n pinned_tweet_id?: string;\n created_at?: string;\n}\n\nexport interface XUserInfo {\n data: XUser;\n includes?: { tweets?: { id: string; text: string }[]; users?: XUser[] };\n}\n\ntype UrlEntity = {\n start: number;\n end: number;\n url: string;\n display_url: string;\n expanded_url: string;\n};\ntype HashtagEntity = { start: number; end: number; tag: string };\ntype MentionEntity = { start: number; end: string; username: string };\n\nexport interface XToken extends OAuth2Token {\n token_type: string;\n expires_in: number;\n access_token: string;\n scope: string;\n refresh_token: string;\n}\n\ntype XErrorResponse = { error: string; error_description: string };\ntype XAPIErrorResponse = {\n errors: { title: string; type: string; detail?: string; status?: number }[];\n};\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/x.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport type { OAuth2Token, Provider } from '../types';\nimport { OAuth2Error } from '../error';\nimport {\n createAuthorizationUri,\n exchangeAuthorizationCode,\n refreshAccessToken,\n revokeToken,\n} from './common';\n\nexport interface XOptions {\n userFields?: UserField[];\n}\n\nconst defaultUserFields = [\n 'id',\n 'name',\n 'profile_image_url',\n 'url',\n 'username',\n 'location',\n 'verified',\n];\n\nexport function createXProvider(options?: XOptions): Provider {\n return {\n authorizationUri: 'https://x.com/i/oauth2/authorize',\n tokenUri: 'https://api.x.com/2/oauth2/token',\n tokenRefreshUri: 'https://api.x.com/2/oauth2/token',\n tokenRevokeUri: 'https://api.x.com/2/oauth2/revoke',\n // https://docs.x.com/x-api/users/user-lookup-me\n userInfoUri: 'https://api.x.com/2/users/me',\n defaultScope: ['users.read', 'tweet.read', 'offline.access'],\n createAuthorizationUri(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n const response = await exchangeAuthorizationCode({\n ...params,\n tokenUri: this.tokenUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n\n const userFields = options?.userFields ?? defaultUserFields;\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n\n const url = new URL(this.userInfoUri);\n userFields.forEach((field) => url.searchParams.append('user.fields', field));\n\n const response = await fetch(this.userInfoUri, { headers });\n if (!response.ok) {\n const { errors } = (await response.json()) as XAPIErrorResponse;\n throw new OAuth2Error(\n response.status,\n 'invalid_request',\n `Failed to fetch user info: ${errors.at(0)?.detail ?? errors.at(0)?.title}`\n );\n }\n const profile = (await response.json()) as XUserInfo;\n return {\n data: profile,\n claims: {\n sub: profile.data.id,\n name: profile.data.name,\n picture: profile.data.profile_image_url,\n email: profile.data.email,\n email_verified: profile.data.verified || false,\n preferred_username: profile.data.username,\n profile: `https://x.com/${profile.data.username}`,\n website: profile.data.url,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const response = await refreshAccessToken({\n ...params,\n tokenUri: this.tokenRefreshUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async revokeToken(params) {\n invariant(this.tokenRevokeUri, 'tokenRevokeUri is required');\n return revokeToken({\n ...params,\n tokenRevokeUri: this.tokenRevokeUri,\n authentication: 'basic',\n });\n },\n };\n}\n\nexport const x = createXProvider();\n\ntype UserField =\n | 'affiliation'\n | 'connection_status'\n | 'created_at'\n | 'description'\n | 'entities'\n | 'id'\n | 'is_identity_verified'\n | 'location'\n | 'most_recent_tweet_id'\n | 'name'\n | 'parody'\n | 'pinned_tweet_id'\n | 'profile_banner_url'\n | 'profile_image_url'\n | 'protected'\n | 'public_metrics'\n | 'receives_your_dm'\n | 'subscription'\n | 'subscription_type'\n | 'url'\n | 'username'\n | 'verified'\n | 'verified_followers_count'\n | 'verified_type'\n | 'withheld';\n\nexport interface XUser {\n id: string;\n name: string;\n email?: string;\n username: string;\n location?: string;\n entities?: {\n url: { urls: UrlEntity[] };\n description: {\n hashtags: HashtagEntity[];\n cashtags: HashtagEntity[];\n mentions: MentionEntity[];\n };\n };\n verified?: boolean;\n description?: string;\n url?: string;\n profile_image_url?: string;\n protected?: boolean;\n pinned_tweet_id?: string;\n created_at?: string;\n}\n\nexport interface XUserInfo {\n data: XUser;\n includes?: { tweets?: { id: string; text: string }[]; users?: XUser[] };\n}\n\ntype UrlEntity = {\n start: number;\n end: number;\n url: string;\n display_url: string;\n expanded_url: string;\n};\ntype HashtagEntity = { start: number; end: number; tag: string };\ntype MentionEntity = { start: number; end: string; username: string };\n\nexport interface XToken extends OAuth2Token {\n token_type: string;\n expires_in: number;\n access_token: string;\n scope: string;\n refresh_token: string;\n}\n\ntype XErrorResponse = { error: string; error_description: string };\ntype XAPIErrorResponse = {\n errors: { title: string; type: string; detail?: string; status?: number }[];\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/x.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { OAuth2Token, Provider } from '../types';\nimport {\n createAuthorizationUri,\n exchangeAuthorizationCode,\n refreshAccessToken,\n revokeToken,\n} from './common';\n\nexport interface XOptions {\n userFields?: UserField[];\n}\n\nconst defaultUserFields = [\n 'id',\n 'name',\n 'profile_image_url',\n 'url',\n 'username',\n 'location',\n 'verified',\n];\n\nexport function createXProvider(options?: XOptions): Provider {\n return {\n authorizationUri: 'https://x.com/i/oauth2/authorize',\n tokenUri: 'https://api.x.com/2/oauth2/token',\n tokenRefreshUri: 'https://api.x.com/2/oauth2/token',\n tokenRevokeUri: 'https://api.x.com/2/oauth2/revoke',\n // https://docs.x.com/x-api/users/user-lookup-me\n userInfoUri: 'https://api.x.com/2/users/me',\n defaultScope: ['users.read', 'tweet.read', 'offline.access'],\n createAuthorizationUri(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n const response = await exchangeAuthorizationCode({\n ...params,\n tokenUri: this.tokenUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n\n const userFields = options?.userFields ?? defaultUserFields;\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n\n const url = new URL(this.userInfoUri);\n userFields.forEach((field) => url.searchParams.append('user.fields', field));\n\n const response = await fetch(this.userInfoUri, { headers });\n if (!response.ok) {\n const { errors } = (await response.json()) as XAPIErrorResponse;\n throw new OAuth2Error(\n response.status,\n 'invalid_request',\n `Failed to fetch user info: ${errors.at(0)?.detail ?? errors.at(0)?.title}`\n );\n }\n const profile = (await response.json()) as XUserInfo;\n return {\n data: profile,\n claims: {\n sub: profile.data.id,\n name: profile.data.name,\n picture: profile.data.profile_image_url,\n email: profile.data.email,\n email_verified: profile.data.verified || false,\n preferred_username: profile.data.username,\n profile: `https://x.com/${profile.data.username}`,\n website: profile.data.url,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const response = await refreshAccessToken({\n ...params,\n tokenUri: this.tokenRefreshUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async revokeToken(params) {\n invariant(this.tokenRevokeUri, 'tokenRevokeUri is required');\n return revokeToken({\n ...params,\n tokenRevokeUri: this.tokenRevokeUri,\n authentication: 'basic',\n });\n },\n };\n}\n\nexport const x = createXProvider();\n\ntype UserField =\n | 'affiliation'\n | 'connection_status'\n | 'created_at'\n | 'description'\n | 'entities'\n | 'id'\n | 'is_identity_verified'\n | 'location'\n | 'most_recent_tweet_id'\n | 'name'\n | 'parody'\n | 'pinned_tweet_id'\n | 'profile_banner_url'\n | 'profile_image_url'\n | 'protected'\n | 'public_metrics'\n | 'receives_your_dm'\n | 'subscription'\n | 'subscription_type'\n | 'url'\n | 'username'\n | 'verified'\n | 'verified_followers_count'\n | 'verified_type'\n | 'withheld';\n\nexport interface XUser {\n id: string;\n name: string;\n email?: string;\n username: string;\n location?: string;\n entities?: {\n url: { urls: UrlEntity[] };\n description: {\n hashtags: HashtagEntity[];\n cashtags: HashtagEntity[];\n mentions: MentionEntity[];\n };\n };\n verified?: boolean;\n description?: string;\n url?: string;\n profile_image_url?: string;\n protected?: boolean;\n pinned_tweet_id?: string;\n created_at?: string;\n}\n\nexport interface XUserInfo {\n data: XUser;\n includes?: { tweets?: { id: string; text: string }[]; users?: XUser[] };\n}\n\ntype UrlEntity = {\n start: number;\n end: number;\n url: string;\n display_url: string;\n expanded_url: string;\n};\ntype HashtagEntity = { start: number; end: number; tag: string };\ntype MentionEntity = { start: number; end: string; username: string };\n\nexport interface XToken extends OAuth2Token {\n token_type: string;\n expires_in: number;\n access_token: string;\n scope: string;\n refresh_token: string;\n}\n\ntype XErrorResponse = { error: string; error_description: string };\ntype XAPIErrorResponse = {\n errors: { title: string; type: string; detail?: string; status?: number }[];\n};\n"],"mappings":"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","names":[]}