modrinth / labrinth

Our Rust-based backend utilizing the actix-web framework to serve Modrinth's API.

Home Page:https://modrinth.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

OAuth 2.0 behavior deviates from standard practice

cyyynthia opened this issue · comments

Describe the bug

The current implementation of the OAuth 2.0 flow deviates in certain key areas making it hard to interop with generic clients. I encountered these while using my own generic OAuth handler.

Most notably,

  • Client authentication (RFC 6749 §2.3.) is done using a non-standard authentication system where the client secret is sent "as-is" as the authentication header. The general standard is to use HTTP Basic authentication, which is mentioned in §2.3.1..
  • The API returns a Bearer token (RFC 6750), but does not support Bearer <token> authorization format despite being the format defined in RFC 6750 §2.1.. Only the prefix-less token is supported.

Steps to reproduce

  1. Create an OAuth application
  2. Initiate an authotization flow
  3. Request the bearer access token (with Authorization: Basic base64(client_id:client_secret))
  4. Request the authenticated user data (with Authorization: Bearer access_token)

Expected behavior

The flow to succeed and to get information about the authenticated user.

Additional context

No response

@cyyynthia Would you be able to review #872 to make sure I implemented it correctly?

Hello! I was just wondering how you worked around this - did you just completely write a new client to interop with Modrinth's implementation? I am currently facing the same issue @cyyynthia

The OAuth client I'm using is already my own, so I could just attach extra logic for Modrinth specifically - I didn't though and I just don't have support yet 😄

Modrinth's OAuth implementation is mostly compliant except for the client secret part. You need to send the client_id in the request payload (probably already happening since many implementations expect client credentials to be sent via the request payload), and the client secret in the Authorization header as a plain string (Authorization: <client_secret>).

In my own implementation I would only have to add a special case to the Authorization header, everything else works as expected using a generic implementation.

Oh thank you! That's what I was missing! I was just about to scour through the codebase but you saved me some time - thank you :]