@gw2me/client 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,21 +2,216 @@
2
2
 
3
3
  This is a client library to interact with the gw2.me API.
4
4
 
5
- ## Usage
5
+ ## Installation
6
+
7
+ ```
8
+ npm i @gw2me/client
9
+ ```
10
+
11
+ ## API
12
+
13
+ This section shows basic examples and concepts on how to use the different functions provided by this library. For all parameters, please check the typescript types.
14
+
15
+ ### `Gw2MeClient`
16
+
17
+ The `Gw2MeClient` class is the main entry point into using this library. You can pass your client id and, for confidential clients, the optional client secret. The second optional parameter takes an options object, which currently can be used to override the `url` of gw2.me, for example when developing against a local instance.
18
+
19
+ All these functions are usually also provided by default OAuth2 libraries. If you just care about accessing the gw2.me specific APIs, you can skip this section and continue to [`Gw2MeApi`](#gw2meapi).
20
+
6
21
 
7
22
  ```ts
8
- import { Gw2MeClient } from '@gw2me/client';
23
+ const client = new Gw2MeClient(client: { clientId: string, clientSecret?: string }, options?: Partial<{ url: string }>)
24
+ ```
25
+
26
+ #### `getAuthorizationUrl`
9
27
 
10
- const client = new Gw2MeClient({ client_id: '<client_id>' });
11
- const url = client.getAuthorizationUrl({ /* ... */ });
28
+ `client.getAuthorizationUrl` can be used to create a authorization url. You can read more about the available parameters on https://gw2.me/dev/docs/access-tokens.
12
29
 
13
- // redirect user to url
30
+ ```ts
31
+ // create auth url
32
+ const authUrl = client.getAuthorizationUrl({
33
+ redirect_uri: 'https://example.com/auth/callback',
34
+ scopes: [Scope.Identify],
35
+ // ...additional properties here
36
+ });
37
+
38
+ // redirect the user to the auth url
39
+ location.href = authUrl;
14
40
  ```
15
41
 
16
- ## Installation
42
+ #### `pushAuthorizationRequest`
43
+
44
+ `client.pushAuthorizationRequest` can be used for Pushed Authorization Requests (PAR). Read more about PAR on https://gw2.me/dev/docs/access-tokens#par.
45
+
46
+ ```ts
47
+ // create pushed authorization request
48
+ const pushedRequest = await client.pushAuthorizationRequest({
49
+ redirect_uri: 'https://example.com/auth/callback',
50
+ scopes: [Scope.Identify],
51
+ // ...additional properties here
52
+ });
17
53
 
54
+ // get url for the pushed authorization request
55
+ const authUrl = client.getAuthorizationUrl(pushedRequest);
56
+
57
+ // redirect the user to the auth url
58
+ location.href = authUrl;
18
59
  ```
19
- npm i @gw2me/client
60
+
61
+ #### `parseAuthorizationResponseSearchParams`
62
+
63
+ `client.parseAuthorizationResponseSearchParams` parses search url parameters to extract the code or error.
64
+
65
+ ```ts
66
+ const searchParams = new URLSearchParams(location.search);
67
+ const { code } = client.parseAuthorizationResponseSearchParams(searchParams);
68
+ ```
69
+
70
+
71
+ #### `getAccessToken`
72
+
73
+ `client.getAccessToken` exchanges an authorization token for an access token, which can be used to call the gw2.me API.
74
+
75
+ ```ts
76
+ const accessToken = await client.getAccessToken({ code });
77
+ ```
78
+
79
+ #### `refreshToken`
80
+
81
+ `client.refreshToken` uses a refresh token to receive a new access token. See https://gw2.me/dev/docs/refresh-tokens for more information on refreshing tokens.
82
+
83
+ ```ts
84
+ const accessToken = await client.refreshToken({ refresh_token });
85
+ ```
86
+
87
+ #### `revokeToken`
88
+
89
+ `client.revokeToken` can be used to revoke a (access or refresh) token that is no longer needed.
90
+
91
+ ```ts
92
+ await client.revokeToken({ token });
93
+ ```
94
+
95
+ #### `introspectToken`
96
+
97
+ `client.introspectToken` returns information about an access or refresh token.
98
+
99
+ ```ts
100
+ const info = await client.introspectToken({ token });
101
+ ```
102
+
103
+ ### `Gw2MeApi`
104
+
105
+ The `Gw2MeApi` class provides access to the gw2.me API. This can be useful even if using a standard OAuth2 library for the initial OAuth2 flow instead of the functions described above.
106
+
107
+ ```ts
108
+ const api = client.api(accessToken);
109
+ ```
110
+
111
+ #### `user`
112
+
113
+ `api.user` returns the current user. See https://gw2.me/dev/docs/users for more information.
114
+
115
+ ```ts
116
+ const user = await api.user();
117
+ ```
118
+
119
+ #### `saveSettings`
120
+
121
+ `api.saveSettings` stores settings for the current user. See https://gw2.me/dev/docs/users for more information.
122
+
123
+ ```ts
124
+ await api.saveSettings({ foo: 'bar' });
125
+ ```
126
+
127
+ #### `accounts`
128
+
129
+ `api.accounts` gets all the authorized Guild Wars 2 accounts for the current user. See https://gw2.me/dev/docs/gw2-api#accounts for more information.
130
+
131
+ ```ts
132
+ const accounts = await api.accounts();
133
+ ```
134
+
135
+ #### `subtoken`
136
+
137
+ `api.subtoken` requests a subtoken for the given account of the user. The subtoken can be used to make authenticated requests against the official Guild Wars 2 API. See https://gw2.me/dev/docs/gw2-api#subtoken for more information.
138
+
139
+ ```ts
140
+ const subtoken = await api.subtoken(accountId);
141
+
142
+ // you can also request a limited set of permissions
143
+ const subtoken = await api.subtoken(accountId, { permissions: ['account'] });
144
+ ```
145
+
146
+ ### FedCM
147
+
148
+ FedCM is a browser API for privacy-preserving federated authentication without the need for third-party cookies and redirects. It is described in more detail on https://gw2.me/dev/docs/fed-cm.
149
+
150
+ ```ts
151
+ // check if FedCM is supported in the current browser
152
+ const isSupported = client.fedCM.isSupported();
153
+
154
+ // initiate a FedCM request
155
+ const { token: code } = await client.fedCM.request({
156
+ scopes: [Scope.Identify],
157
+ // ...additional properties here
158
+ });
159
+
160
+ // The received code can now be exchanged for an access token using the regular client functions
161
+ const accessToken = await client.getAccessToken({ code });
162
+ ```
163
+
164
+ ### PCKE
165
+
166
+ Proof Key for Code Exchange (PKCE) is a method to prevent code interception attacks. You can read more about it on https://gw2.me/dev/docs/access-tokens#pkce. It is highly recommended you use PKCE.
167
+
168
+ This library provides a helper functions to generate code challenges.
169
+
170
+ ```ts
171
+ import { generatePKCEPair } from '@gw2me/client/pkce';
172
+
173
+ const { code_verifier, challenge } = await generatePKCEPair();
174
+
175
+ // pass the generated challenge when creating the authorization url
176
+ const authUrl = client.getAuthorizationUrl({
177
+ redirect_uri: 'https://example.com/auth/callback',
178
+ scopes: [Scope.Identify],
179
+ ...challenge,
180
+ // ...additional properties here
181
+ });
182
+
183
+ // ...
184
+
185
+ // later use the verifier when exchanging the code for the access token
186
+ const accessToken = await client.getAccessToken({ code, code_verifier });
187
+ ```
188
+
189
+ ### DPoP
190
+
191
+ DPoP is a method to prevent replay attacks by binding the access token to the client. You can read more about DPoP on https://gw2.me/dev/docs/access-tokens#dpop.
192
+
193
+ All client functions that can use DPoP take a `dpop` callback as parameter, which should return the DPoP proof. This library exports some basic functions to get you started with using DPoP.
194
+
195
+ ```ts
196
+ import { generateDPoPKeyPair, createDPoPJwt, jwkThumbprint } from '@gw2me/client/dpop';
197
+
198
+ // create a DPoP key pair
199
+ const dpop = await generateDPoPKeyPair();
200
+ // TODO: store the dpop keys securely, as dpop bound tokens can only be used in combination with these keys
201
+
202
+ // create a DPoP bound authorization url by passing the thumbprint of the public key
203
+ const authUrl = client.getAuthorizationUrl({
204
+ // ...
205
+ dpop_jkt: await jwkThumbprint(dpop.publicKey)
206
+ });
207
+
208
+ // ...
209
+
210
+ // later exchange code for access token
211
+ const accessToken = await client.getAccessToken({
212
+ code,
213
+ dpop: (params) => createDPoPJwt(params, dpop),
214
+ });
20
215
  ```
21
216
 
22
217
  ## License
@@ -0,0 +1 @@
1
+ function a(r){let e=r instanceof ArrayBuffer?new Uint8Array(r):r;return btoa(String.fromCharCode(...e)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}export{a};
@@ -0,0 +1 @@
1
+ var f=a=>{throw TypeError(a)};var d=(a,b,c)=>b.has(a)||f("Cannot "+c);var g=(a,b,c)=>(d(a,b,"read from private field"),c?c.call(a):b.get(a)),h=(a,b,c)=>b.has(a)?f("Cannot add the same private member more than once"):b instanceof WeakSet?b.add(a):b.set(a,c),i=(a,b,c,e)=>(d(a,b,"write to private field"),e?e.call(a,c):b.set(a,c),c),j=(a,b,c)=>(d(a,b,"access private method"),c);export{g as a,h as b,i as c,j as d};
package/dist/dpop.js CHANGED
@@ -1 +1 @@
1
- "use strict";var u=Object.defineProperty;var d=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var m=(t,r)=>{for(var e in r)u(t,e,{get:r[e],enumerable:!0})},P=(t,r,e,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of w(r))!l.call(t,n)&&n!==e&&u(t,n,{get:()=>r[n],enumerable:!(a=d(r,n))||a.enumerable});return t};var A=t=>P(u({},"__esModule",{value:!0}),t);var K={};m(K,{createDPoPJwt:()=>h,generateDPoPKeyPair:()=>b,jwkThumbprint:()=>C});module.exports=A(K);function o(t){let r=t instanceof ArrayBuffer?new Uint8Array(t):t;return btoa(String.fromCharCode(...r)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function b(){return crypto.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!1,["sign"])}async function h({htm:t,htu:r,nonce:e,accessToken:a},n){let c=JSON.stringify({alg:"ES256",typ:"dpop+jwt",jwk:await p(n.publicKey)}),y=JSON.stringify({iat:Math.floor(Date.now()/1e3),jti:o(crypto.getRandomValues(new Uint8Array(32))),htm:t,htu:r,nonce:e,ath:a?o(await crypto.subtle.digest("SHA-256",i(a))):void 0}),s=`${o(i(c))}.${o(i(y))}`,f={name:"ECDSA",hash:"SHA-256"},g=o(await crypto.subtle.sign(f,n.privateKey,i(s)));return`${s}.${g}`}var S=new TextEncoder;function i(t){return S.encode(t)}async function p(t){let{kty:r,e,k:a,n,x:c,y,crv:s}=await crypto.subtle.exportKey("jwk",t);return{e,k:a,crv:s,kty:r,n,x:c,y}}async function C(t){let r=JSON.stringify(await p(t)),e=await crypto.subtle.digest("SHA-256",i(r));return o(e)}0&&(module.exports={createDPoPJwt,generateDPoPKeyPair,jwkThumbprint});
1
+ import{a as e}from"./chunk-EXOYQILJ.js";import"./chunk-OSZ7DOEH.js";function d(){return crypto.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!1,["sign"])}async function m({htm:t,htu:n,nonce:r,accessToken:a},i){let y=JSON.stringify({alg:"ES256",typ:"dpop+jwt",jwk:await u(i.publicKey)}),c=JSON.stringify({iat:Math.floor(Date.now()/1e3),jti:e(crypto.getRandomValues(new Uint8Array(32))),htm:t,htu:n,nonce:r,ath:a?e(await crypto.subtle.digest("SHA-256",o(a))):void 0}),s=`${e(o(y))}.${e(o(c))}`,p={name:"ECDSA",hash:"SHA-256"},g=e(await crypto.subtle.sign(p,i.privateKey,o(s)));return`${s}.${g}`}var w=new TextEncoder;function o(t){return w.encode(t)}async function u(t){let{kty:n,e:r,k:a,n:i,x:y,y:c,crv:s}=await crypto.subtle.exportKey("jwk",t);return{e:r,k:a,crv:s,kty:n,n:i,x:y,y:c}}async function P(t){let n=JSON.stringify(await u(t)),r=await crypto.subtle.digest("SHA-256",o(n));return e(r)}export{m as createDPoPJwt,d as generateDPoPKeyPair,P as jwkThumbprint};
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { O as Options, a as DPoPCallback, S as Scope, C as ClientInfo } from './
2
2
  export { D as DPoPParams } from './types-yBP8cksw.js';
3
3
 
4
4
  interface UserResponse {
5
+ sub: string;
5
6
  user: {
6
7
  id: string;
7
8
  name: string;
@@ -14,6 +15,7 @@ interface AccountsResponse {
14
15
  accounts: {
15
16
  id: string;
16
17
  name: string;
18
+ shared: boolean;
17
19
  verified?: boolean;
18
20
  displayName?: string | null;
19
21
  }[];
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var C=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var G=n=>{throw TypeError(n)};var E=(n,e)=>{for(var t in e)C(n,t,{get:e[t],enumerable:!0})},B=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of W(e))!L.call(n,o)&&o!==t&&C(n,o,{get:()=>e[o],enumerable:!(r=j(e,o))||r.enumerable});return n};var M=n=>B(C({},"__esModule",{value:!0}),n);var U=(n,e,t)=>e.has(n)||G("Cannot "+t);var a=(n,e,t)=>(U(n,e,"read from private field"),t?t.call(n):e.get(n)),m=(n,e,t)=>e.has(n)?G("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(n):e.set(n,t),_=(n,e,t,r)=>(U(n,e,"write to private field"),r?r.call(n,t):e.set(n,t),t),i=(n,e,t)=>(U(n,e,"access private method"),t);var J={};E(J,{Gw2MeApi:()=>T,Gw2MeClient:()=>q,Gw2MeError:()=>d,Gw2MeOAuthError:()=>x,Scope:()=>I});module.exports=M(J);var I=(u=>(u.Identify="identify",u.Email="email",u.Accounts="accounts",u.Accounts_Verified="accounts.verified",u.Accounts_DisplayName="accounts.displayName",u.GW2_Account="gw2:account",u.GW2_Inventories="gw2:inventories",u.GW2_Characters="gw2:characters",u.GW2_Tradingpost="gw2:tradingpost",u.GW2_Wallet="gw2:wallet",u.GW2_Unlocks="gw2:unlocks",u.GW2_Pvp="gw2:pvp",u.GW2_Wvw="gw2:wvw",u.GW2_Builds="gw2:builds",u.GW2_Progression="gw2:progression",u.GW2_Guilds="gw2:guilds",u))(I||{});var d=class extends Error{},x=class extends d{constructor(t,r,o){super(`Received ${t}`+(r?`: ${r}`:"")+(o?` (${o})`:""));this.error=t;this.error_description=r;this.error_uri=o}};async function l(n){if(await z(n),!(n.headers.get("Content-Type")==="application/json"))throw new d("gw2.me did not return a valid JSON response");return n.json()}async function z(n){if(!n.ok){let e;throw n.headers.get("Content-Type")==="application/json"&&(e=(await n.json()).error_description),new d(`gw2.me returned an error: ${e??"Unknown error"}`)}}var f,S,R,T=class{constructor(e,t){this.access_token=e;this.options=t;m(this,f)}user(){return i(this,f,R).call(this,"api/user").then(e=>fetch(e)).then(l)}saveSettings(e){return i(this,f,R).call(this,"api/user/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}).then(t=>fetch(t)).then(z)}accounts(){return i(this,f,R).call(this,"api/accounts").then(e=>fetch(e)).then(l)}subtoken(e,t){let r=i(this,f,S).call(this,`api/accounts/${e}/subtoken`);return t?.permissions&&r.searchParams.set("permissions",t.permissions.join(",")),i(this,f,R).call(this,r).then(o=>fetch(o)).then(l)}};f=new WeakSet,S=function(e){return new URL(e,this.options?.url||"https://gw2.me/")},R=async function(e,t){let r=e instanceof URL?e:i(this,f,S).call(this,e),o=this.options?.dpop,s=new Headers(t?.headers);return s.set("Authorization",`${o?"DPoP":"Bearer"} ${this.access_token}`),o&&s.set("DPoP",await o({htm:t?.method??"GET",htu:r.toString(),accessToken:this.access_token})),new Request(r,{cache:"no-cache",...t,headers:s})};var A,v,O=class{constructor(e,t){m(this,A);m(this,v);_(this,A,e),_(this,v,t)}isSupported(){return typeof window<"u"&&"IdentityCredential"in window}request({scopes:e,mediation:t,signal:r,mode:o,code_challenge:s,code_challenge_method:h}){if(!this.isSupported())throw new d("FedCM is not supported");return navigator.credentials.get({mediation:t,signal:r,identity:{providers:[{configURL:a(this,A),clientId:a(this,v),fields:[e.includes("identify")&&"name",e.includes("email")&&"email"].filter(Boolean),nonce:`${h}:${s}`,params:{scope:e.join(" "),code_challenge:s,code_challenge_method:h}}],mode:o}})}};A=new WeakMap,v=new WeakMap;var c,b,p,P,y,q=class{constructor(e,t){this.options=t;m(this,p);m(this,c);m(this,b);_(this,c,e),_(this,b,new O(i(this,p,P).call(this,"/fed-cm/config.json"),e.client_id))}getAuthorizationUrl(e){let t="request_uri"in e?new URLSearchParams({client_id:a(this,c).client_id,response_type:"code",request_uri:e.request_uri}):$(a(this,c).client_id,e);return i(this,p,P).call(this,`/oauth2/authorize?${t.toString()}`).toString()}async pushAuthorizationRequest(e){let t=i(this,p,P).call(this,"/oauth2/par"),r={"Content-Type":"application/x-www-form-urlencoded"};e.dpop&&(r.DPoP=await e.dpop({htm:"POST",htu:t.toString()}));let o=$(a(this,c).client_id,e);return a(this,c).client_secret&&(r.Authorization=i(this,p,y).call(this)),await fetch(t,{method:"POST",headers:r,body:o,cache:"no-store"}).then(l)}async getAccessToken({code:e,token_type:t,redirect_uri:r,code_verifier:o,dpop:s}){let h=new URLSearchParams({grant_type:"authorization_code",code:e,client_id:a(this,c).client_id,redirect_uri:r}),g={"Content-Type":"application/x-www-form-urlencoded"};a(this,c).client_secret&&(g.Authorization=i(this,p,y).call(this)),o&&h.set("code_verifier",o);let w=i(this,p,P).call(this,"/api/token");return s&&(g.DPoP=await s({htm:"POST",htu:w.toString(),accessToken:t==="DPoP"?e:void 0})),await fetch(w,{method:"POST",headers:g,body:h,cache:"no-store"}).then(l)}async refreshToken({refresh_token:e,refresh_token_type:t,dpop:r}){let o=new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:a(this,c).client_id}),s={"Content-Type":"application/x-www-form-urlencoded"};a(this,c).client_secret&&(s.Authorization=i(this,p,y).call(this));let h=i(this,p,P).call(this,"/api/token");return r&&(s.DPoP=await r({htm:"POST",htu:h.toString(),accessToken:t==="DPoP"?e:void 0})),await fetch(h,{method:"POST",headers:s,body:o,cache:"no-store"}).then(l)}async revokeToken({token:e}){let t=new URLSearchParams({token:e}),r={"Content-Type":"application/x-www-form-urlencoded"};a(this,c).client_secret&&(r.Authorization=i(this,p,y).call(this)),await fetch(i(this,p,P).call(this,"/api/token/revoke"),{method:"POST",cache:"no-store",headers:r,body:t}).then(l)}async introspectToken({token:e}){let t=new URLSearchParams({token:e}),r={"Content-Type":"application/x-www-form-urlencoded"};return a(this,c).client_secret&&(r.Authorization=i(this,p,y).call(this)),await fetch(i(this,p,P).call(this,"/api/token/introspect"),{method:"POST",cache:"no-store",headers:r,body:t}).then(l)}parseAuthorizationResponseSearchParams(e){let t=i(this,p,P).call(this,"/").origin,r=e.get("iss");if(!r)throw new d("Issuer Identifier verification failed: parameter `iss` is missing");if(r!==t)throw new d(`Issuer Identifier verification failed: expected "${t}", got "${r}"`);let o=e.get("error");if(o){let g=e.get("error_description")??void 0,w=e.get("error_uri")??void 0;throw new x(o,g,w)}let s=e.get("code");if(!s)throw new d("Parameter `code` is missing");let h=e.get("state")||void 0;return{code:s,state:h}}api(e,t){return new T(e,{...this.options,...t})}get fedCM(){return a(this,b)}};c=new WeakMap,b=new WeakMap,p=new WeakSet,P=function(e){return new URL(e,this.options?.url||"https://gw2.me/")},y=function(){if(!a(this,c).client_secret)throw new d("client_secret is required");return`Basic ${btoa(`${a(this,c).client_id}:${a(this,c).client_secret}`)}`};function $(n,{redirect_uri:e,scopes:t,state:r,code_challenge:o,code_challenge_method:s,dpop_jkt:h,prompt:g,include_granted_scopes:w,verified_accounts_only:D}){let k=new URLSearchParams({client_id:n,response_type:"code",redirect_uri:e,scope:t.join(" ")});return r&&k.append("state",r),o&&s&&(k.append("code_challenge",o),k.append("code_challenge_method",s)),h&&k.append("dpop_jkt",h),g&&k.append("prompt",g),w&&k.append("include_granted_scopes","true"),D&&k.append("verified_accounts_only","true"),k}0&&(module.exports={Gw2MeApi,Gw2MeClient,Gw2MeError,Gw2MeOAuthError,Scope});
1
+ import{a as s,b as m,c as _,d as o}from"./chunk-OSZ7DOEH.js";var z=(p=>(p.Identify="identify",p.Email="email",p.Accounts="accounts",p.Accounts_Verified="accounts.verified",p.Accounts_DisplayName="accounts.displayName",p.GW2_Account="gw2:account",p.GW2_Inventories="gw2:inventories",p.GW2_Characters="gw2:characters",p.GW2_Tradingpost="gw2:tradingpost",p.GW2_Wallet="gw2:wallet",p.GW2_Unlocks="gw2:unlocks",p.GW2_Pvp="gw2:pvp",p.GW2_Wvw="gw2:wvw",p.GW2_Builds="gw2:builds",p.GW2_Progression="gw2:progression",p.GW2_Guilds="gw2:guilds",p))(z||{});var h=class extends Error{},v=class extends h{constructor(t,n,r){super(`Received ${t}`+(n?`: ${n}`:"")+(r?` (${r})`:""));this.error=t;this.error_description=n;this.error_uri=r}};async function l(u){if(await C(u),!(u.headers.get("Content-Type")==="application/json"))throw new h("gw2.me did not return a valid JSON response");return u.json()}async function C(u){if(!u.ok){let e;throw u.headers.get("Content-Type")==="application/json"&&(e=(await u.json()).error_description),new h(`gw2.me returned an error: ${e??"Unknown error"}`)}}var f,U,x,b=class{constructor(e,t){this.access_token=e;this.options=t;m(this,f)}user(){return o(this,f,x).call(this,"api/user").then(e=>fetch(e)).then(l)}saveSettings(e){return o(this,f,x).call(this,"api/user/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}).then(t=>fetch(t)).then(C)}accounts(){return o(this,f,x).call(this,"api/accounts").then(e=>fetch(e)).then(l)}subtoken(e,t){let n=o(this,f,U).call(this,`api/accounts/${e}/subtoken`);return t?.permissions&&n.searchParams.set("permissions",t.permissions.join(",")),o(this,f,x).call(this,n).then(r=>fetch(r)).then(l)}};f=new WeakSet,U=function(e){return new URL(e,this.options?.url||"https://gw2.me/")},x=async function(e,t){let n=e instanceof URL?e:o(this,f,U).call(this,e),r=this.options?.dpop,i=new Headers(t?.headers);return i.set("Authorization",`${r?"DPoP":"Bearer"} ${this.access_token}`),r&&i.set("DPoP",await r({htm:t?.method??"GET",htu:n.toString(),accessToken:this.access_token})),new Request(n,{cache:"no-cache",...t,headers:i})};var R,T,O=class{constructor(e,t){m(this,R);m(this,T);_(this,R,e),_(this,T,t)}isSupported(){return typeof window<"u"&&"IdentityCredential"in window}request({scopes:e,mediation:t,signal:n,mode:r,code_challenge:i,code_challenge_method:d}){if(!this.isSupported())throw new h("FedCM is not supported");return navigator.credentials.get({mediation:t,signal:n,identity:{providers:[{configURL:s(this,R),clientId:s(this,T),fields:[e.includes("identify")&&"name",e.includes("email")&&"email"].filter(Boolean),nonce:`${d}:${i}`,params:{scope:e.join(" "),code_challenge:i,code_challenge_method:d}}],mode:r}})}};R=new WeakMap,T=new WeakMap;var a,A,c,P,y,S=class{constructor(e,t){this.options=t;m(this,c);m(this,a);m(this,A);_(this,a,e),_(this,A,new O(o(this,c,P).call(this,"/fed-cm/config.json"),e.client_id))}getAuthorizationUrl(e){let t="request_uri"in e?new URLSearchParams({client_id:s(this,a).client_id,response_type:"code",request_uri:e.request_uri}):q(s(this,a).client_id,e);return o(this,c,P).call(this,`/oauth2/authorize?${t.toString()}`).toString()}async pushAuthorizationRequest(e){let t=o(this,c,P).call(this,"/oauth2/par"),n={"Content-Type":"application/x-www-form-urlencoded"};e.dpop&&(n.DPoP=await e.dpop({htm:"POST",htu:t.toString()}));let r=q(s(this,a).client_id,e);return s(this,a).client_secret&&(n.Authorization=o(this,c,y).call(this)),await fetch(t,{method:"POST",headers:n,body:r,cache:"no-store"}).then(l)}async getAccessToken({code:e,token_type:t,redirect_uri:n,code_verifier:r,dpop:i}){let d=new URLSearchParams({grant_type:"authorization_code",code:e,client_id:s(this,a).client_id,redirect_uri:n}),g={"Content-Type":"application/x-www-form-urlencoded"};s(this,a).client_secret&&(g.Authorization=o(this,c,y).call(this)),r&&d.set("code_verifier",r);let w=o(this,c,P).call(this,"/api/token");return i&&(g.DPoP=await i({htm:"POST",htu:w.toString(),accessToken:t==="DPoP"?e:void 0})),await fetch(w,{method:"POST",headers:g,body:d,cache:"no-store"}).then(l)}async refreshToken({refresh_token:e,refresh_token_type:t,dpop:n}){let r=new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:s(this,a).client_id}),i={"Content-Type":"application/x-www-form-urlencoded"};s(this,a).client_secret&&(i.Authorization=o(this,c,y).call(this));let d=o(this,c,P).call(this,"/api/token");return n&&(i.DPoP=await n({htm:"POST",htu:d.toString(),accessToken:t==="DPoP"?e:void 0})),await fetch(d,{method:"POST",headers:i,body:r,cache:"no-store"}).then(l)}async revokeToken({token:e}){let t=new URLSearchParams({token:e}),n={"Content-Type":"application/x-www-form-urlencoded"};s(this,a).client_secret&&(n.Authorization=o(this,c,y).call(this)),await fetch(o(this,c,P).call(this,"/api/token/revoke"),{method:"POST",cache:"no-store",headers:n,body:t}).then(l)}async introspectToken({token:e}){let t=new URLSearchParams({token:e}),n={"Content-Type":"application/x-www-form-urlencoded"};return s(this,a).client_secret&&(n.Authorization=o(this,c,y).call(this)),await fetch(o(this,c,P).call(this,"/api/token/introspect"),{method:"POST",cache:"no-store",headers:n,body:t}).then(l)}parseAuthorizationResponseSearchParams(e){let t=o(this,c,P).call(this,"/").origin,n=e.get("iss");if(!n)throw new h("Issuer Identifier verification failed: parameter `iss` is missing");if(n!==t)throw new h(`Issuer Identifier verification failed: expected "${t}", got "${n}"`);let r=e.get("error");if(r){let g=e.get("error_description")??void 0,w=e.get("error_uri")??void 0;throw new v(r,g,w)}let i=e.get("code");if(!i)throw new h("Parameter `code` is missing");let d=e.get("state")||void 0;return{code:i,state:d}}api(e,t){return new b(e,{...this.options,...t})}get fedCM(){return s(this,A)}};a=new WeakMap,A=new WeakMap,c=new WeakSet,P=function(e){return new URL(e,this.options?.url||"https://gw2.me/")},y=function(){if(!s(this,a).client_secret)throw new h("client_secret is required");return`Basic ${btoa(`${s(this,a).client_id}:${s(this,a).client_secret}`)}`};function q(u,{redirect_uri:e,scopes:t,state:n,code_challenge:r,code_challenge_method:i,dpop_jkt:d,prompt:g,include_granted_scopes:w,verified_accounts_only:I}){let k=new URLSearchParams({client_id:u,response_type:"code",redirect_uri:e,scope:t.join(" ")});return n&&k.append("state",n),r&&i&&(k.append("code_challenge",r),k.append("code_challenge_method",i)),d&&k.append("dpop_jkt",d),g&&k.append("prompt",g),w&&k.append("include_granted_scopes","true"),I&&k.append("verified_accounts_only","true"),k}export{b as Gw2MeApi,S as Gw2MeClient,h as Gw2MeError,v as Gw2MeOAuthError,z as Scope};
package/dist/pkce.js CHANGED
@@ -1 +1 @@
1
- "use strict";var o=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var g=(e,r)=>{for(var n in r)o(e,n,{get:r[n],enumerable:!0})},f=(e,r,n,c)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of i(r))!d.call(e,a)&&a!==n&&o(e,a,{get:()=>r[a],enumerable:!(c=l(r,a))||c.enumerable});return e};var s=e=>f(o({},"__esModule",{value:!0}),e);var u={};g(u,{generatePKCEPair:()=>h});module.exports=s(u);function t(e){let r=e instanceof ArrayBuffer?new Uint8Array(e):e;return btoa(String.fromCharCode(...r)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}async function h(){let e=new Uint8Array(32);crypto.getRandomValues(e);let r=t(e),n=new TextEncoder,c=await crypto.subtle.digest("SHA-256",n.encode(r));return{code_verifier:r,challenge:{code_challenge_method:"S256",code_challenge:t(new Uint8Array(c))}}}0&&(module.exports={generatePKCEPair});
1
+ import{a as e}from"./chunk-EXOYQILJ.js";import"./chunk-OSZ7DOEH.js";async function a(){let n=new Uint8Array(32);crypto.getRandomValues(n);let r=e(n),c=new TextEncoder,o=await crypto.subtle.digest("SHA-256",c.encode(r));return{code_verifier:r,challenge:{code_challenge_method:"S256",code_challenge:e(new Uint8Array(o))}}}export{a as generatePKCEPair};
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@gw2me/client",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "gw2.me client library",
5
- "main": "./dist/index.js",
6
- "types": "./dist/index.d.ts",
5
+ "type": "module",
7
6
  "exports": {
8
7
  "./package.json": "./package.json",
9
8
  ".": {
@@ -43,16 +42,16 @@
43
42
  "@gw2treasures/eslint-config": "0.1.0",
44
43
  "@gw2treasures/publish-package": "0.1.0-rc.0",
45
44
  "@gw2treasures/tsconfig": "0.0.1",
46
- "eslint": "9.25.1",
47
- "tsup": "8.4.0",
45
+ "eslint": "9.27.0",
46
+ "tsup": "8.5.0",
48
47
  "typescript": "5.8.3",
49
- "typescript-eslint": "8.31.0",
50
- "undici-types": "7.8.0"
48
+ "typescript-eslint": "8.32.1",
49
+ "undici-types": "7.10.0"
51
50
  },
52
51
  "scripts": {
53
52
  "build": "pnpm run clean && if test $CI; then pnpm run build:ci; else pnpm run build:local; fi",
54
- "build:local": "tsup src/index.ts src/dpop.ts src/pkce.ts && tsc --emitDeclarationOnly --declaration --declarationMap",
55
- "build:ci": "tsup src/index.ts src/dpop.ts src/pkce.ts --minify --dts",
53
+ "build:local": "tsup src/index.ts src/dpop.ts src/pkce.ts --format esm && tsc --emitDeclarationOnly --declaration --declarationMap",
54
+ "build:ci": "tsup src/index.ts src/dpop.ts src/pkce.ts --format esm --minify --dts",
56
55
  "clean": "rm -rf dist/",
57
56
  "lint": "eslint src",
58
57
  "publish-package": "gw2treasures-publish-package"