@@ -58,6 +58,17 @@ type oauthRevokeBody struct {
5858 ClientID string `json:"client_id" form:"client_id"`
5959}
6060
61+ // normalizeOAuthScope collapses space-separated OAuth scopes (e.g. "read write") to the
62+ // highest-privilege single scope. This tolerates Swagger UI sending compound scope strings.
63+ func normalizeOAuthScope (raw string ) string {
64+ for _ , part := range strings .Fields (raw ) {
65+ if part == "write" {
66+ return "write"
67+ }
68+ }
69+ return strings .TrimSpace (raw )
70+ }
71+
6172// normalizeClientID lowercases and ensures the 0x prefix on a client_id (developer app address).
6273func normalizeClientID (raw string ) string {
6374 id := strings .ToLower (strings .TrimSpace (raw ))
@@ -77,6 +88,20 @@ type oauthTokenCacheEntry struct {
7788
7889// --- Handlers ---
7990
91+ // v1OAuthAuthorizeRedirect handles GET /v1/oauth/authorize
92+ // Redirects the browser to the Audius app consent page, forwarding all query parameters.
93+ func (app * ApiServer ) v1OAuthAuthorizeRedirect (c * fiber.Ctx ) error {
94+ base := app .config .AudiusAppUrl
95+ if base == "" {
96+ base = "https://audius.co"
97+ }
98+ target := base + "/oauth/auth"
99+ if qs := string (c .Request ().URI ().QueryString ()); qs != "" {
100+ target += "?" + qs
101+ }
102+ return c .Redirect (target , fiber .StatusFound )
103+ }
104+
80105// v1OAuthAuthorize handles POST /v1/oauth/authorize
81106// Called by the audius.co consent screen after the user authenticates.
82107func (app * ApiServer ) v1OAuthAuthorize (c * fiber.Ctx ) error {
@@ -99,7 +124,8 @@ func (app *ApiServer) v1OAuthAuthorize(c *fiber.Ctx) error {
99124 return oauthError (c , fiber .StatusBadRequest , "invalid_request" , "code_challenge_method must be S256" )
100125 }
101126
102- if body .Scope != "read" && body .Scope != "write" {
127+ scope := normalizeOAuthScope (body .Scope )
128+ if scope != "read" && scope != "write" {
103129 return oauthError (c , fiber .StatusBadRequest , "invalid_request" , "scope must be 'read' or 'write'" )
104130 }
105131
@@ -165,7 +191,7 @@ func (app *ApiServer) v1OAuthAuthorize(c *fiber.Ctx) error {
165191 }
166192
167193 // 4. If scope is write, check for existing approved grant
168- if body . Scope == "write" {
194+ if scope == "write" {
169195 var grantExists bool
170196 err = app .pool .QueryRow (c .Context (), `
171197 SELECT EXISTS (
@@ -193,7 +219,7 @@ func (app *ApiServer) v1OAuthAuthorize(c *fiber.Ctx) error {
193219 _ , err = app .writePool .Exec (c .Context (), `
194220 INSERT INTO oauth_authorization_codes (code, client_id, user_id, redirect_uri, code_challenge, code_challenge_method, scope)
195221 VALUES ($1, $2, $3, $4, $5, $6, $7)
196- ` , code , clientID , int32 (userId ), body .RedirectURI , body .CodeChallenge , body .CodeChallengeMethod , body . Scope )
222+ ` , code , clientID , int32 (userId ), body .RedirectURI , body .CodeChallenge , body .CodeChallengeMethod , scope )
197223 if err != nil {
198224 app .logger .Error ("Failed to insert auth code" , zap .Error (err ))
199225 return oauthError (c , fiber .StatusInternalServerError , "server_error" , "Failed to create authorization code" )
0 commit comments