Logo

dev-resources.site

for different kinds of informations.

Declaring multiple sets of scopes for the same provider with Devise and OmniAuth in Rails

Published at
1/6/2021
Categories
rails
oauth
devise
Author
phacks
Categories
3 categories in total
rails
open
oauth
open
devise
open
Author
6 person written this
phacks
open
Declaring multiple sets of scopes for the same provider with Devise and OmniAuth in Rails

Photo by Stephen Phillips - Hostreviews.co.uk on Unsplash

If you’re familiar with the Rails ecosystem, the names Devise and OmniAuth might ring a bell: the former is a gem that handles (nearly) everything related to authentication; coupled with the latter, it makes implementing popular Social Login providers (e.g. Login with Facebook, or Twitter, or GitHub
) a breeze.

They can also be used to abstract away the whole OAuth dance that developers need to wrangle with every time they want to connect to a third-party API. We use it extensively at Orbit to authenticate with the GitHub, Twitter, Discourse, and Slack APIs, allowing us to build powerful integrations on top of those.

Take Slack, for example. Our Slack App connects Orbit to our users’ Slack workspaces to send notifications and provide a handy /orbit command.

The command /orbit add github:phacks added a new member to our Orbit community

Here’s what the OmniAuth provider for that looks like:

config.omniauth :slack,
                ENV['SLACK_CLIENT_ID'],
                ENV['SLACK_CLIENT_SECRET'],
                scope: 'commands,chat:write,chat:write.public,channels:read'
Enter fullscreen mode Exit fullscreen mode

In some cases, however, you might need two different sets of scopes for two distinct integrations with the same service.

As we’re building our Slack Integration, which will allow users to gather and analyze the activity in their community Slack, we realized that we needed users to authorize to Slack twice: once on their team Slack, for the Slack App, and once on their community Slack, for the Slack integration. Moreover, the required scopes would differ wildly: as the Slack App needs to post messages and respond to slash commands, the Slack integration would only need to listen to events (e.g. somebody joined, or posted a message).

Adding both sets of scopes to our single OmniAuth provider would have worked, but it is considered (rightly) a security risk to ask for too broad a scope: in our case, the Slack App has no business listening to new messages and the Slack Integration shouldn’t be able to post messages in a channel.

So we needed to create two sets of scopes (one for the App, one for the Integration) for the same provider (Slack).

The first step was to rename our existing provider (the one above) to :slack_app. By doing this however, we lose the implicit binding of that provider to the Slack strategy—which we can hopefully add back with the strategy_class option:

config.omniauth :slack_app,
                ENV['SLACK_APP_CLIENT_ID'],
                ENV['SLACK_APP_CLIENT_SECRET'],
                scope: 'commands,chat:write,chat:write.public,channels:read',
                strategy_class: OmniAuth::Strategies::Slack
Enter fullscreen mode Exit fullscreen mode

This gets us close, but not yet there: this config will set the provider attribute of the OAuth return payload to slack, not slack_app—meaning the callback route cannot know whether this particular user authorized the Slack App or the Slack Integration.

We can get around this by adding the name: slack_app option, which will do two things:

  • Set the provider attribute of the OAuth return payload to the right value, and
  • Change the OAuth callback route to /users/auth/slack_app/callback instead of /users/auth/slack/callback. (If you’re curious, here’s the bit of code in OmniAuth that’s responsible for inferring the callback URL.)

After changing the app/controllers/users/omniauth_callbacks_controller.rb to reflect the change in the URL (slack becomes slack_app), everything is running smoothly again.

We can now add our second provider for the Slack Integration, with its distinct name and scope.

config.omniauth :slack_app,
                ENV['SLACK_APP_CLIENT_ID'],
                ENV['SLACK_APP_CLIENT_SECRET'],
                name: 'slack_app',
                scope: 'commands,chat:write,chat:write.public,channels:read',
                strategy_class: OmniAuth::Strategies::Slack

config.omniauth :slack_integration,
                ENV['SLACK_INTEGRATION_CLIENT_ID'],
                ENV['SLACK_INTEGRATION_CLIENT_SECRET'],
                name: 'slack_integration',
                scope:
                  'channels:history,channels:read,reactions:read,users:read.email,users.profile:read',
                strategy_class: OmniAuth::Strategies::Slack
Enter fullscreen mode Exit fullscreen mode

Tada! Our users can now authorize only minimal scopes for the App, the Integration, our both—a win for security!

OAuth can often feel confusing, and I want to take this opportunity to thank the Devise and OmniAuth maintainers and contributors who are doing a remarkable job to make it easier for the rest of us.

Hope this article can help folks facing the same issues we did!

devise Article's
30 articles in total
Favicon
Devise not accepting JSON Token
Favicon
Reset password mailer implementation in rails 7 api, devise_token_auth, and sendgrid-ruby.
Favicon
Ruby on Rails: Autenticação utilizando Devise + Keycloak
Favicon
How to Install Devise
Favicon
Warden of Hanami - hanami.rb basic authentication
Favicon
Devise raise validations error when new and old passwords are same
Favicon
Hooking in to Devise controller actions
Favicon
Rails ćŸș瀎 Part 06 -- devise ă§ăƒ­ă‚°ă‚€ăƒłă‚’ă—ăŸäžŠă§ă€API UT ă‚’ć©ă
Favicon
Using Devise and SendGrid to send confirmation email on rails app
Favicon
Omniauth without Devise
Favicon
Setting Up User Auth With React and Rails Minus The JWT Headache
Favicon
How to Backup Android Contacts to Mac Devices?
Favicon
Signout Users
Favicon
Rails 7 + Devise + Log out
Favicon
How to Add ToS Agreement Checkbox to Your Rails App using Devise?
Favicon
Multi-Factor Authentication for Rails with WebAuthn and Devise
Favicon
Omniauth + Devise + Rails Tutorial
Favicon
Rails 7.0.0alpha2, esbuild, tailwind and devise
Favicon
Devise: User + Profile
Favicon
Rails redirect user to the previous page after signup or login
Favicon
Devise-ing A Backend...
Favicon
Devise Cheat Sheet
Favicon
Rails Authentication with Devise
Favicon
Extending the default user devise
Favicon
Using Devise for User Auth
Favicon
install gem invisible_captcha with devise
Favicon
Adding a field to your sign-up form with Devise
Favicon
Declaring multiple sets of scopes for the same provider with Devise and OmniAuth in Rails
Favicon
Devise and JWT in Rails
Favicon
Customize Devise’s flash messages

Featured ones: