Skip to content

Commit a17b031

Browse files
committed
require write scope in oauth handling for write paths
1 parent b416973 commit a17b031

2 files changed

Lines changed: 52 additions & 40 deletions

File tree

api/auth_middleware.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ func (app *ApiServer) authMiddleware(c *fiber.Ctx) error {
270270
if entry, ok := app.lookupOAuthAccessToken(c, bearerToken); ok {
271271
if myId == 0 || entry.UserID == myId {
272272
wallet = strings.ToLower(entry.ClientID)
273+
c.Locals("oauthScope", entry.Scope)
273274
if myId == 0 {
274275
myId = entry.UserID
275276
c.Locals("myId", int(entry.UserID))
@@ -339,6 +340,17 @@ func (app *ApiServer) requireAuthMiddleware(c *fiber.Ctx) error {
339340
return c.Next()
340341
}
341342

343+
// Middleware that asserts the request carries write scope when authenticated via
344+
// an OAuth PKCE token. Non-OAuth auth methods (signature, api_access_key) are
345+
// always allowed through. Must be placed after authMiddleware.
346+
func (app *ApiServer) requireWriteScope(c *fiber.Ctx) error {
347+
if scope, ok := c.Locals("oauthScope").(string); ok && scope != "" && scope != "write" {
348+
return fiber.NewError(fiber.StatusForbidden, "OAuth token scope insufficient: write scope required")
349+
}
350+
351+
return c.Next()
352+
}
353+
342354
// Get a user from their wallet address.
343355
//
344356
// Note: Do NOT use this with `getAuthedWallet()` to infer the current user.

api/server.go

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ func NewApiServer(config config.Config) *ApiServer {
359359
for _, g := range []fiber.Router{v1, v1Full} {
360360
// Users
361361
g.Get("/users", app.v1Users)
362-
g.Post("/users", app.requireAuthMiddleware, app.postV1Users)
362+
g.Post("/users", app.requireAuthMiddleware, app.requireWriteScope, app.postV1Users)
363363
g.Get("/users/address", app.v1UserIdsByAddresses)
364364
g.Get("/users/search", app.v1UsersSearch)
365365
g.Get("/users/unclaimed_id", app.v1UsersUnclaimedId)
@@ -392,11 +392,11 @@ func NewApiServer(config config.Config) *ApiServer {
392392
g.Get("/users/:userId/balance/history", app.v1UsersBalanceHistory)
393393
g.Get("/users/:userId/managers", app.v1UsersManagers)
394394
g.Get("/users/:userId/managed_users", app.v1UsersManagedUsers)
395-
g.Post("/users/:userId/grants", app.requireAuthMiddleware, app.postV1UsersGrant)
396-
g.Delete("/users/:userId/grants/:address", app.requireAuthMiddleware, app.deleteV1UsersGrant)
397-
g.Post("/users/:userId/managers", app.requireAuthMiddleware, app.postV1UsersManager)
398-
g.Delete("/users/:userId/managers/:managerUserId", app.requireAuthMiddleware, app.deleteV1UsersManager)
399-
g.Post("/users/:userId/grants/approve", app.requireAuthMiddleware, app.postV1UsersApproveGrant)
395+
g.Post("/users/:userId/grants", app.requireAuthMiddleware, app.requireWriteScope, app.postV1UsersGrant)
396+
g.Delete("/users/:userId/grants/:address", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1UsersGrant)
397+
g.Post("/users/:userId/managers", app.requireAuthMiddleware, app.requireWriteScope, app.postV1UsersManager)
398+
g.Delete("/users/:userId/managers/:managerUserId", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1UsersManager)
399+
g.Post("/users/:userId/grants/approve", app.requireAuthMiddleware, app.requireWriteScope, app.postV1UsersApproveGrant)
400400
g.Get("/users/:userId/mutuals", app.v1UsersMutuals)
401401
g.Get("/users/:userId/reposts", app.v1UsersReposts)
402402
g.Get("/users/:userId/related", app.v1UsersRelated)
@@ -444,17 +444,17 @@ func NewApiServer(config config.Config) *ApiServer {
444444
g.Get("/users/:userId/developer-apps", app.v1UsersDeveloperApps)
445445
g.Get("/users/:userId/withdrawals/download", app.requireAuthForUserId, app.v1UsersWithdrawalsDownloadCsv)
446446
g.Get("/users/:userId/withdrawals/download/json", app.requireAuthForUserId, app.v1UsersWithdrawalsDownloadJson)
447-
g.Post("/users/:userId/follow", app.requireAuthMiddleware, app.postV1UserFollow)
448-
g.Delete("/users/:userId/follow", app.requireAuthMiddleware, app.deleteV1UserFollow)
449-
g.Post("/users/:userId/subscribe", app.requireAuthMiddleware, app.postV1UserSubscribe)
450-
g.Delete("/users/:userId/subscribe", app.requireAuthMiddleware, app.deleteV1UserSubscribe)
451-
g.Post("/users/:userId/mute", app.requireAuthMiddleware, app.postV1UserMute)
452-
g.Delete("/users/:userId/mute", app.requireAuthMiddleware, app.deleteV1UserMute)
453-
g.Put("/users/:userId", app.requireAuthMiddleware, app.putV1User)
447+
g.Post("/users/:userId/follow", app.requireAuthMiddleware, app.requireWriteScope, app.postV1UserFollow)
448+
g.Delete("/users/:userId/follow", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1UserFollow)
449+
g.Post("/users/:userId/subscribe", app.requireAuthMiddleware, app.requireWriteScope, app.postV1UserSubscribe)
450+
g.Delete("/users/:userId/subscribe", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1UserSubscribe)
451+
g.Post("/users/:userId/mute", app.requireAuthMiddleware, app.requireWriteScope, app.postV1UserMute)
452+
g.Delete("/users/:userId/mute", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1UserMute)
453+
g.Put("/users/:userId", app.requireAuthMiddleware, app.requireWriteScope, app.putV1User)
454454

455455
// Tracks
456456
g.Get("/tracks", app.v1Tracks)
457-
g.Post("/tracks", app.requireAuthMiddleware, app.postV1Tracks)
457+
g.Post("/tracks", app.requireAuthMiddleware, app.requireWriteScope, app.postV1Tracks)
458458
g.Get("/tracks/search", app.v1TracksSearch)
459459
g.Get("/tracks/unclaimed_id", app.v1TracksUnclaimedId)
460460

@@ -479,16 +479,16 @@ func NewApiServer(config config.Config) *ApiServer {
479479
g.Get("/tracks/:trackId/access-info", app.v1TrackAccessInfo)
480480
g.Get("/tracks/:trackId/remixes", app.v1TrackRemixes)
481481
g.Get("/tracks/:trackId/reposts", app.v1TrackReposts)
482-
g.Post("/tracks/:trackId/reposts", app.requireAuthMiddleware, app.postV1TrackRepost)
483-
g.Delete("/tracks/:trackId/reposts", app.requireAuthMiddleware, app.deleteV1TrackRepost)
482+
g.Post("/tracks/:trackId/reposts", app.requireAuthMiddleware, app.requireWriteScope, app.postV1TrackRepost)
483+
g.Delete("/tracks/:trackId/reposts", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1TrackRepost)
484484
g.Get("/tracks/:trackId/stems", app.v1TrackStems)
485485
g.Get("/tracks/:trackId/favorites", app.v1TrackFavorites)
486-
g.Post("/tracks/:trackId/favorites", app.requireAuthMiddleware, app.postV1TrackFavorite)
487-
g.Delete("/tracks/:trackId/favorites", app.requireAuthMiddleware, app.deleteV1TrackFavorite)
488-
g.Post("/tracks/:trackId/shares", app.requireAuthMiddleware, app.postV1TrackShare)
489-
g.Post("/tracks/:trackId/downloads", app.requireAuthMiddleware, app.postV1TrackDownload)
490-
g.Put("/tracks/:trackId", app.requireAuthMiddleware, app.putV1Track)
491-
g.Delete("/tracks/:trackId", app.requireAuthMiddleware, app.deleteV1Track)
486+
g.Post("/tracks/:trackId/favorites", app.requireAuthMiddleware, app.requireWriteScope, app.postV1TrackFavorite)
487+
g.Delete("/tracks/:trackId/favorites", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1TrackFavorite)
488+
g.Post("/tracks/:trackId/shares", app.requireAuthMiddleware, app.requireWriteScope, app.postV1TrackShare)
489+
g.Post("/tracks/:trackId/downloads", app.requireAuthMiddleware, app.requireWriteScope, app.postV1TrackDownload)
490+
g.Put("/tracks/:trackId", app.requireAuthMiddleware, app.requireWriteScope, app.putV1Track)
491+
g.Delete("/tracks/:trackId", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1Track)
492492
g.Get("/tracks/:trackId/comments", app.v1TrackComments)
493493
g.Get("/tracks/:trackId/comment_count", app.v1TrackCommentCount)
494494
g.Get("/tracks/:trackId/comment-count", app.v1TrackCommentCount)
@@ -501,7 +501,7 @@ func NewApiServer(config config.Config) *ApiServer {
501501

502502
// Playlists
503503
g.Get("/playlists", app.v1Playlists)
504-
g.Post("/playlists", app.requireAuthMiddleware, app.postV1Playlists)
504+
g.Post("/playlists", app.requireAuthMiddleware, app.requireWriteScope, app.postV1Playlists)
505505
g.Get("/playlists/search", app.v1PlaylistsSearch)
506506
g.Get("/playlists/unclaimed_id", app.v1PlaylistsUnclaimedId)
507507
g.Get("/playlists/unclaimed-id", app.v1PlaylistsUnclaimedId)
@@ -514,14 +514,14 @@ func NewApiServer(config config.Config) *ApiServer {
514514
g.Get("/playlists/:playlistId", app.v1Playlist)
515515
g.Get("/playlists/:playlistId/stream", app.v1PlaylistStream)
516516
g.Get("/playlists/:playlistId/reposts", app.v1PlaylistReposts)
517-
g.Post("/playlists/:playlistId/reposts", app.requireAuthMiddleware, app.postV1PlaylistRepost)
518-
g.Delete("/playlists/:playlistId/reposts", app.requireAuthMiddleware, app.deleteV1PlaylistRepost)
517+
g.Post("/playlists/:playlistId/reposts", app.requireAuthMiddleware, app.requireWriteScope, app.postV1PlaylistRepost)
518+
g.Delete("/playlists/:playlistId/reposts", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1PlaylistRepost)
519519
g.Get("/playlists/:playlistId/favorites", app.v1PlaylistFavorites)
520-
g.Post("/playlists/:playlistId/favorites", app.requireAuthMiddleware, app.postV1PlaylistFavorite)
521-
g.Delete("/playlists/:playlistId/favorites", app.requireAuthMiddleware, app.deleteV1PlaylistFavorite)
522-
g.Post("/playlists/:playlistId/shares", app.requireAuthMiddleware, app.postV1PlaylistShare)
523-
g.Put("/playlists/:playlistId", app.requireAuthMiddleware, app.putV1Playlist)
524-
g.Delete("/playlists/:playlistId", app.requireAuthMiddleware, app.deleteV1Playlist)
520+
g.Post("/playlists/:playlistId/favorites", app.requireAuthMiddleware, app.requireWriteScope, app.postV1PlaylistFavorite)
521+
g.Delete("/playlists/:playlistId/favorites", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1PlaylistFavorite)
522+
g.Post("/playlists/:playlistId/shares", app.requireAuthMiddleware, app.requireWriteScope, app.postV1PlaylistShare)
523+
g.Put("/playlists/:playlistId", app.requireAuthMiddleware, app.requireWriteScope, app.putV1Playlist)
524+
g.Delete("/playlists/:playlistId", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1Playlist)
525525
g.Get("/playlists/:playlistId/tracks", app.v1PlaylistTracks)
526526

527527
// Explore
@@ -543,8 +543,8 @@ func NewApiServer(config config.Config) *ApiServer {
543543
g.Delete("/developer-apps/:address", app.deleteV1UsersDeveloperApp)
544544
g.Post("/developer_apps/:address/access-keys/deactivate", app.postV1UsersDeveloperAppAccessKeyDeactivate)
545545
g.Post("/developer-apps/:address/access-keys/deactivate", app.postV1UsersDeveloperAppAccessKeyDeactivate)
546-
g.Post("/developer_apps/:address/register-api-key", app.requireAuthMiddleware, app.postV1UsersDeveloperAppRegisterApiKey)
547-
g.Post("/developer-apps/:address/register-api-key", app.requireAuthMiddleware, app.postV1UsersDeveloperAppRegisterApiKey)
546+
g.Post("/developer_apps/:address/register-api-key", app.requireAuthMiddleware, app.requireWriteScope, app.postV1UsersDeveloperAppRegisterApiKey)
547+
g.Post("/developer-apps/:address/register-api-key", app.requireAuthMiddleware, app.requireWriteScope, app.postV1UsersDeveloperAppRegisterApiKey)
548548
g.Post("/developer_apps/:address/access-keys", app.postV1UsersDeveloperAppAccessKey)
549549
g.Post("/developer-apps/:address/access-keys", app.postV1UsersDeveloperAppAccessKey)
550550

@@ -570,15 +570,15 @@ func NewApiServer(config config.Config) *ApiServer {
570570
// Comments
571571
g.Get("/comments/unclaimed_id", app.v1CommentsUnclaimedId)
572572
g.Get("/comments/unclaimed-id", app.v1CommentsUnclaimedId)
573-
g.Post("/comments", app.requireAuthMiddleware, app.postV1Comment)
573+
g.Post("/comments", app.requireAuthMiddleware, app.requireWriteScope, app.postV1Comment)
574574
g.Get("/comments/:commentId", app.v1Comment)
575-
g.Put("/comments/:commentId", app.requireAuthMiddleware, app.putV1Comment)
576-
g.Delete("/comments/:commentId", app.requireAuthMiddleware, app.deleteV1Comment)
577-
g.Post("/comments/:commentId/react", app.requireAuthMiddleware, app.postV1CommentReact)
578-
g.Delete("/comments/:commentId/react", app.requireAuthMiddleware, app.deleteV1CommentReact)
579-
g.Post("/comments/:commentId/pin", app.requireAuthMiddleware, app.postV1CommentPin)
580-
g.Delete("/comments/:commentId/pin", app.requireAuthMiddleware, app.deleteV1CommentPin)
581-
g.Post("/comments/:commentId/report", app.requireAuthMiddleware, app.postV1CommentReport)
575+
g.Put("/comments/:commentId", app.requireAuthMiddleware, app.requireWriteScope, app.putV1Comment)
576+
g.Delete("/comments/:commentId", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1Comment)
577+
g.Post("/comments/:commentId/react", app.requireAuthMiddleware, app.requireWriteScope, app.postV1CommentReact)
578+
g.Delete("/comments/:commentId/react", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1CommentReact)
579+
g.Post("/comments/:commentId/pin", app.requireAuthMiddleware, app.requireWriteScope, app.postV1CommentPin)
580+
g.Delete("/comments/:commentId/pin", app.requireAuthMiddleware, app.requireWriteScope, app.deleteV1CommentPin)
581+
g.Post("/comments/:commentId/report", app.requireAuthMiddleware, app.requireWriteScope, app.postV1CommentReport)
582582

583583
// Tips
584584
g.Get("/tips", app.v1Tips)

0 commit comments

Comments
 (0)