API update - May 20 2019 — Guild Wars 2 Forums

API update - May 20 2019

edited May 21, 2019 in API Development

Hello all,

I put together a quick change to the API to allow some more fine-tuned control over sharing API keys a few weeks ago, and I had a chance to deploy it today. I have added an endpoint for creating what I'm calling "subtokens".

Recent API Updates


First, here is the change in list format:

  • Added the /v2/createsubtoken endpoint

Subtokens


As a warning: this change's uses are niche.

A subtoken is a special API key that can be used anywhere a normal API key can be used. It is simply a wrapper around a regular API key with reduced permissions. It can be created by accessing /v2/createsubtoken with several options:

  • Subset of permissions (e.g. account, inventories)
  • Expire time
  • List of urls that can be accessed (optional: if no urls are provided, then all urls are allowed)

Here is an example that shows the full functionality:

GET https://api.guildwars2.com/v2/createsubtoken?permissions=account
&expire=2019-12-25%2012:34:56
&urls=/v2/characters/My%20Cool%20Character,/v2/account/home/cats

Authorization: Bearer MY_API_KEY

The API will respond with:

{
  "subtoken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ3YlRodVdNNGExMUduZlpYSTdaa0pHck52
  SVVPUWhMejZHTXpOeE9TUC1rIiwiaWF0IjoxNTU4Mzk3OTUwLCJleHAiOjE1NzczMDYwOTYsInBlcm1pc3Npb25zIjpbIn
  Byb2dyZXNzaW9uIiwiYWNjb3VudCIsInVubG9ja3MiXSwidXJscyI6WyIvdjIvY2hhcmFjdGVycy9NeSUyMENvb2wlMjBD
  aGFyYWN0ZXIiLCIvdjIvYWNjb3VudC9ob21lL2NhdHMiXX0.UdLlafgo8lxkb1Hn88paZT83aw_9mHEYVZJLDgObNSc"
}

I can then see what cats I have unlocked with this large subtoken

GET https://api.guildwars2.com/v2/account/home/cats

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ3YlRodVdNNGExMUduZlpYSTdaa
0pHck52SVVPUWhMejZHTXpOeE9TUC1rIiwiaWF0IjoxNTU4Mzk3OTUwLCJleHAiOjE1NzczMDYwOTYsInBlcm1pc3Npb25zI
jpbInByb2dyZXNzaW9uIiwiYWNjb3VudCIsInVubG9ja3MiXSwidXJscyI6WyIvdjIvY2hhcmFjdGVycy9NeSUyMENvb2wlM
jBDaGFyYWN0ZXIiLCIvdjIvYWNjb3VudC9ob21lL2NhdHMiXX0.UdLlafgo8lxkb1Hn88paZT83aw_9mHEYVZJLDgObNSc

and get normal results.

The request will be rejected if:

  • The reduced permissions do not meet the permission requirements of the endpoint
  • The subtoken time is expired
  • The request url does not match the restricted url set (unless there are no url restrictions)
  • The original API key which was used to create the subtoken is deleted

Subtoken uses


As I admitted earlier, there aren't a huge amount of uses for a subtoken. Here are the two use-cases I considered while making this change:

First and foremost, subtokens lets an app (App 1) accept & store an API key from a player and then pass that API key on to another app (App 2) with more control over what App 2 can do with the player's key.

The other case is for savvy users who want more control over what they share with their API key. They can use the endpoint to generate subtokens to hand over to apps with, e.g. expire times or restrictions to certain character endpoints.

I'd love to hear thoughts and feedback for this change, as well as any bug reports.

Thanks!
Snider

EDIT: Added a bit about deleting the original API Key

Tagged:

Comments

  • Illconceived Was Na.9781Illconceived Was Na.9781 Member ✭✭✭✭
    edited May 21, 2019

    Thanks for the post, @Daniel Snider.6241
    I love the regular communication about plans and changes.

    I'm looking forward to seeing how the fan-devs use the subtoken. Sounds very promising.

    "Face the facts. Then act on them. It's ...the only doctrine I have to offer you, & it's harder than you'd think, because I swear humans seem hardwired to do anything but. Face the facts. Don't pray, don't wish, ...FACE THE FACTS. THEN act." — Quellcrist Falconer

  • DragonFury.6243DragonFury.6243 Member ✭✭✭✭

    I am loving these changes
    Love the communications

  • Karasu.9483Karasu.9483 Member ✭✭

    This is a ridiculously awesome and underrated update. Thank you so much for your work in continuing to expand and your support of the API.

  • A quick update:

    Some users have already pointed out some bugs with the new /v2/createsubtoken endpoint. Those have been fixed.
    I have also changed /v2/tokeninfo to understand subtokens, given a recent schema version parameter (e.g. ?v=latest).

    If you see any more issues, let me know. Thanks!

  • Elrey.5472Elrey.5472 Member ✭✭

    Can we create endless subtokens?

  • StevenL.3761StevenL.3761 Member ✭✭
    edited May 22, 2019

    Hey I'd be careful with allowing GET with createsubtoken because it has different semantics that (maybe some) clients use to decide whether to cache the response.

    In general a GET should be idempotent (asking a question should not change the answer).

  • @Elrey.5472 said:
    Can we create endless subtokens?

    No. I decided to start with a required expire time and a maximum. I figured the endpoint should start more restrictive, then wait to see if we need to loosen the restrictions depending on app developers' needs.

  • @StevenL.3761 said:
    Hey I'd be careful with allowing GET with createsubtoken because it has different semantics that (maybe some) clients use to decide whether to cache the response.

    In general a GET should be idempotent (asking a question should not change the answer).

    You're right, it really should be POST. I chose GET because for API internal reasons POST is much harder and I wanted to get a fast turnaround on this feature.

    That said, I think GET is OK here because it's still a stateless call: the subtoken parameters are just stored in the subtoken itself. Nothing is written and no state is modified on the API server-side when /v2/createsubtoken is accessed.

  • StevenL.3761StevenL.3761 Member ✭✭
    edited May 30, 2019

    Yep that sounds totally fine then.

    I'm implementing access to this endpoint now and I noticed that it's possible to create a token that has no permissions. It's also possible to create a token with an "exp" that is before its "iat".

    Example: GET /v2/createsubtoken?expire=2018-05-30T13:08:13

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
    .eyJzdWIiOiJZZEVvTnRjQkJ1YnF5UmxoZWdPZC1iWGktMUxfd3NxQm1aV0x5YTlXd09ZIiwiaWF0IjoxNTU5MjIxNjkzLCJleHAiOjE1Mjc2ODU2OTMsInBlcm1pc3Npb25zIjpbXX0
    ._Jsmtm8bUkqIWthjcIEG8eNAfM4Npp8x5Br5rsglMvI
    

    Not even /v2/tokeninfo accepts this token. I'm not sure if the service should be handing out tokens that are practically useless?

  • Hmm. Some non-static endpoints require an access key but do not require any permissions. Off the top of my head, I believe /v2/tokeninfo doesn't require any permissions. Maybe that endpoint was built that way because it assumes account is always set.


    I'm not sure why you'd want to generate a token that expires in the past. I could have it error when exp < iat, but there's not a whole lot of benefit other than helpful sanity checking for the user of the endpoint.


    Also, if I have time at some point (and I remember), I'll put some cache-control properties in the response headers to tell the client not to cache the endpoint since it does change on every refresh due to iat being encoded in the response.

  • StevenL.3761StevenL.3761 Member ✭✭

    Thanks for the detailed response.

    Off the top of my head, I believe /v2/tokeninfo doesn't require any permissions.

    I've been trying some things and I've seen tokeninfo return 403 Forbidden under a few circumstances:

    • Token is expired (exp < now())
    • "urls" is used but does not include "/v2/tokeninfo"
    • Race condition?

    I'm not sure about the last one but if you do:

    let subtoken = GET /v2/createsubtoken
    let tokeninfo = GET /v2/tokeninfo?access_token={subtoken}
    

    The second call sometimes fails. Adding a delay between the first and second call seems to help. I don't know how that's possible if the first call does not write state?

  • StevenL.3761StevenL.3761 Member ✭✭
    edited June 4, 2019

    @Daniel Snider.6241 is it possible that /v2/createsubtoken and /v2/tokeninfo run on different machines with different system clocks? Is it possible that those clocks are not synchronized? I often get "Invalid access token" from tokeninfo immediately after the subtoken is created. If there's no state then I'm guessing you use the "iat" claim to check if the token is valid... or some weird signing algorithm that's also time-sensitive.

©2010–2018 ArenaNet, LLC. All rights reserved. Guild Wars, Guild Wars 2, Heart of Thorns, Guild Wars 2: Path of Fire, ArenaNet, NCSOFT, the Interlocking NC Logo, and all associated logos and designs are trademarks or registered trademarks of NCSOFT Corporation. All other trademarks are the property of their respective owners.