wavy / wavyfm-docs

wavy.fm developer documentation
https://wavy.fm/developers
Other
7 stars 1 forks source link

Endpoint request: User through Discord id #4

Closed th0mk closed 3 years ago

th0mk commented 3 years ago

I'm not sure if wavy.fm plans to allow one API account to see info about public users without having them explicitly allowing that application to do so, but if that's the case below endpoint would good to have:

For any potential Discord bot that wants to get information from wavy.fm, but doesn't know who that user is on wavy.fm yet, it would be nice to have an endpoint that allows you to get a wavy.fm user through their Discord id.

My suggestion privacy wise would be that this should probably only work if a user has 'Show Discord account' enabled on their profile. Ideally, this option would then also be shown on the connections page and clarified, for example with something like 'Allow other users and external applications to see my Discord profile'.

Whether this would be checked by default is up to you, but it would be nice if the user is made aware that the option exists after connecting their account. If a Discord bot would try to find a wavy.fm account but fails to do so, it will probably direct the user to go to their connection page and mark that option. Or it could potentially provide a different login option

aramperes commented 3 years ago

I'll unpack this in two different features:


Public Identifiers

There are a few ways to lookup users uniquely. The ones that apply to all users, regardless of their privacy settings are:

In addition, you could lookup users via Discord ID, so long as (1) the profile is public and (2) their Discord connection is "visible" (we'll make sure to make this more obvious in the Connection settings, thanks for the suggestion).

The 'lookup' endpoint will probably serve as our generic GET endpoint for users, and will not have any authorization required. I'm thinking something like this:

GET /v1beta/users

with 3 possible query parameters (mutually exclusive):

id=070f13d4-1645-4516-ae8b-372a97b86b48
username=Aram
discord=104749643715387392

The response format would be the same for all three. However, the privacy settings of the user will change the available fields:

That means, if your Discord bot wants to find a wavy.fm profile by Discord ID, you should handle the 404 case with a message like this:

Could not find a wavy.fm account associated with your Discord. Make sure your Discord is connected and public in your Connection settings, and that your profile is visible to everyone in your Privacy settings.

This covers your use-case, but I don't think it's the best UX since you need to explain to users how to make their Discord public. It also forces users to make their Discord public, and makes it impossible to use private profiles with your bot.

Authorization on that endpoint would be optional -- passing an access token will return results based on the user's access. For example, looking up a blocked profile while using an access token would act as if the profile is private.

Protected Resources

You could also make all your users authorize your app/bot on wavy.fm through the OAuth flow. For example:

  1. Tell users to connect their wavy.fm account to your bot through a link (which opens a wavy.fm Authorization page in their browser, explaining what information would be given to your bot)
  2. User reads through the Authorization page and grants or rejects the form.
  3. Browser is redirected to a website you own, and retrieves the token from the query parameters (all of this is part of OAuth 2.0, I'm skipping over details).
  4. Website stores the association between the Discord ID and the wavy.fm access/refresh tokens. All of this is in a database shared by your website and bot.
  5. When the user uses a command like !fm, it looks for the access/refresh tokens through the Discord ID in your shared database, and you can retrieve info about the user through the access token.

Since users can change their Discord IDs on wavy.fm, you should make sure that the Discord ID you stored for the tokens still matches the one returned by the token endpoint every time you make a request.


The second approach is more complex to implement on your side, but it allows users with private profiles to use it for example. I'm all ears if you have other suggestions!

th0mk commented 3 years ago

I would be fine if users would authorize through an authentication flow, but there is just one issue. We don't have a website to use for this. All we have right now is a documentation site hosted through Github Pages. While a web app would be cool to have in the future, I'm focused on other things right now.

Last.fm offers a desktop authentication flow that I have implemented. You basically generate an authentication token, the user logs in for that token and you can request a session key once if they authenticate. (If you want to test this on my bot, the command is .fmlogin)

I'm fine with either routes. When a user currently adds their account to my bot (through both login methods) anyone can get their last.fm username if they know their discord id. This is by design, because users often check each others music tastes and the bot is meant to be used in group conversations. Every command response also links back to their account. Using a private account would not make a lot of sense in this context, although it is something I want to support in the future.

th0mk commented 3 years ago

Edit: Comment was offtopic for this issue. Continued in #9

aramperes commented 3 years ago

The first pass of this endpoint (Get Public Profile) will be:

https://wavy.fm/api/v1beta/users/wavyfm:user:id:070f13d4-1645-4516-ae8b-372a97b86b48
https://wavy.fm/api/v1beta/users/wavyfm:user:username:Aram
https://wavy.fm/api/v1beta/users/wavyfm:user:discord:104749643715387392

This endpoint will be accessible by both Client Credentials and Authorization Code auth flows. Only public profiles can be returned, private/quarantined profiles will throw a 403 (even if the auth'd user's profile is being requested).

For simplicity's sake, we'll allow retrieving a user by their Discord ID even if they are not showing their Discord ID on their profile. However, it will not be returned in the model if they didn't set it to be shown.

This is the model I'm thinking of:

{
  "uri": "wavyfm:user:id:..."
  "id": "..."
  "username": "..."
  "join_time": "..." // Date RFC3339
  "profile": {
    "avatar": "..." // URL without lease
    "avatar_small": "..." // URL without lease
    "discord": { // Nullable
      "id": "..."
      "display_name": "..."
    }
    "spotify": { // Nullable
      "id": "..."
      "display_name": "..."
    }
    "country": "..." // Nullable
    "biography": "..." // Nullable
    "twitter": "..." // Nullable
    "instagram": "..." // Nullable
  }
}

A separate endpoint (TBD) should be used to get the current user's profile using the Authorization Code flow, because it will allow returning the user's private profile (+ some additional properties, if given access).

aramperes commented 3 years ago

This is now available through the URI format wavyfm:user:discord:<discord ID>. It only works if the profile is public. Private profiles will go through other auth flows.