microsoftgraph / msgraph-sample-rubyrailsapp

This sample demonstrates how to use the Microsoft Graph REST API to access data in Office 365 from Ruby on Rails apps.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Following the tutorial exactly has callback URL errors

GeordieGuy opened this issue · comments

Following this tutorial for Rails to the letter doesn't result in a functional app. The callback URL for authentication is either wrong or there's something else missing. Following it exactly and attempting to log in raises the error;

AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application: '37869d77-ef74-43f7-a135-208dcc7997e9'.

Callback URL for application is http://localhost:3000/auth/microsoft_graph_auth/callback

Ruby 2.6.3
Rails 6.0.2.1

I just recently went through this tutorial and confirmed the steps. When you click the sign in button, and the browser goes off to login.microsoftonline.com, what's the full URL?

Moving this to the ruby tutorial repo.

@GeordieGuy do you still need help with this?

commented

Hi @jasonjoh, I think I have a similar error:
No route matches [POST] "/auth/microsoft_graph_auth"
My routes.rb here:

Rails.application.routes.draw do
  get 'calendar/index'
  get 'home/index'
  root 'home#index'

  # Add future routes here

  # Add route for OmniAuth callback
  match '/auth/:provider/callback', to: 'auth#callback', via: [:get, :post]
  get 'auth/signout'
end

I couldnt find the error. I followed the tutorial exactly as it says.

Ruby 2.6.5p114
Rails 6.0.2.1

The funny thing is that if you use the msgraph-training-rubyrailsapp/Demos/03-add-msgraph/ code, with your APP_ID and APP_SECRET, it works!

Can you help me please? Thanks!

Hmm. It should be going to /auth/microsoft_graph_auth/callback, not /auth/microsoft_graph_auth. Can you find any differences if you diff your project with the msgraph-training-rubyrailsapp/Demos/03-add-msgraph/ code? Using a tool like WinMerge?

I just recently repeated this tutorial from scratch, and didn't hit any issues. Granted I've done this numerous times, so it's possible I've glossed over some step unknowingly.

commented

Nop.. No differences. I did a diff from a shell in linux.
Well, the only differences are in the Gemfile with gems updated.
My Gemfile:

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.5'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.2.0'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use Puma as the app server
gem 'puma', '~> 3.11'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# See https://github.com/rails/execjs#readme for more supported runtimes
gem 'duktape'
# Use CoffeeScript for .coffee assets and views
gem 'coffee-rails', '~> 4.2'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use ActiveStorage variant
# gem 'mini_magick', '~> 4.8'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.1.0', require: false

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]

  # Rubocop
  gem 'rubocop', require: false
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15', '< 4.0'
  gem 'selenium-webdriver'
  # Easy installation and use of chromedriver to run system tests with Chrome
  gem 'chromedriver-helper'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

gem 'omniauth-oauth2', '~> 1.6'

gem 'httparty', '~> 0.17.1'

gem 'nokogiri', '~> 1.10.7'

gem 'activerecord-session_store', '~> 1.1'

gem "omniauth-rails_csrf_protection", "~> 0.1.2"

NOTE: I had to change de ruby version. Which version do you have of Ruby and Rails?

I'm on Windows, and currently I have:

ruby 2.6.5p114 (2019-10-01 revision 67812) [x64-mingw32]
Rails 6.0.2.2
  • What were the differences?
  • Did you use the same app ID for both projects?
commented

Yes. I used same app ID for both projects.

I'm starting a new rails new graph-tutorial from scratch. I'm trying to test my app, following the tutorial, before the step: Storing the tokens.
So my callback for now is:

def callback
  # Access the authentication hash for omniauth
  data = request.env['omniauth.auth']

  # Temporary for testing!
  render json: data.to_json
end

When I go to http://localhost:3000/ I have the following error:

ActionView::Template::Error (The asset "application.js" is not present in the asset pipeline.):
    15:     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"
    16:       integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
    17:     <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    18:     <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
    19:   </head>
    20:
    21:   <body>

app/views/layouts/application.html.erb:18

I'm still with Ruby 2.6.5p114 and Rails 6.0.2.1 on MacOS Catalina.

Didn't you have this error when you started from scratch?

Ah yep, I did. This is a Rails change. Change the javascript_include_tag to javascript_pack_tag.

commented

Yup! I read that on stackoverflow and I did it.
I was finishing the tutorial, and now it works! :D

Maybe you can add a note in the tutorial about javascript_pack_tag if javascript_include_tag doesn't work properly. Because that was the only mistake I had.

Thank you very much @jasonjoh!

I've changed it in the refresh, which hasn't been merged yet. Glad you got it working!

Hi @jasonjoh
I have the similar issue following the tutorial or cloning the completed tutorial.

For both the the cloned demo or the following the tutorial steps, when I click "Click here to sign in" I get a rails error page.

No route matches [GET] "/auth/microsoft_graph_auth"

Focusing on the demo cloned from GitHub.

I pulled the latest version: Latest commit e5a253d on Apr 15
e5a253d

It has the following routes file (omitting comments):

Rails.application.routes.draw do
  get 'calendar/index'
  get 'calendar', to: 'calendar#index'
  get 'home/index'
  root 'home#index'

  match '/auth/:provider/callback', to: 'auth#callback', via: [:get, :post]
  get 'auth/signout'
end

And rails routes -c auth returns the following.

~/Dev/msgraph-training-rubyrailsapp/demo/graph-tutorial(master*) » rails routes -c auth                                                                                                                                                                  mark@Marks-MacBook-Pro
      Prefix Verb     URI Pattern                        Controller#Action
             GET|POST /auth/:provider/callback(.:format) auth#callback
auth_signout GET      /auth/signout(.:format)            auth#signout

First confusing point is that there is no route that corresponds to the path: POST /auth/:provider or GET /auth/:provider

Secondly, the error page says no route matches GET, but the link in app/views/home/index.html.erb is a POST link.

<%= link_to "Click here to sign in", "/auth/microsoft_graph_auth", method: :post, class: "btn btn-primary btn-large" %>

I can see that it is using the data-method attribute when I inspect the HTML:
<a class="btn btn-primary btn-large" rel="nofollow" data-method="post" href="/auth/microsoft_graph_auth">Click here to sign in</a>

Even if it did correctly submit a POST request through js (data-method=POST), I don't know how it would know where to direct it without the corresponding route.

I see there was a massive commit on the 15th of April. I skimmed through it, but I don't see any breaking changes in the main files mentioned above.

Am I miss understanding the intended behaviour of the sign in link? Any help would be greatly appreciated. Thanks

@markhallen what Rails and Ruby version are you using?

The cloned repo is as per the Gemfile:

=> Booting Puma
=> Rails 6.0.2.2 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.3 (ruby 2.6.5-p114), codename: Mysterious Traveller

The follow through is a slightly later version:

=> Booting Puma
=> Rails 6.0.3.1 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.5 (ruby 2.7.0-p0), codename: Mysterious Traveller

Both exhibit exactly the same behaviour.

And you get this error when you click sign-in, or after you sign-in and consent?

Also which browser are you using?

Firefox 77

So to explain what's going on with the routes.rb and using a POST instead of GET:

The sample uses OmniAuth, which has support for multiple auth providers on an app. They use this /auth/:provider/callback route so whichever provider is being used gets inserted where :provider is. It then sends that to the callback method inside the AuthController. This is ONLY for the auth callback - when the user finishes signing in and consenting, and the browser goes back to the app.

The request to /auth/microsoft_graph_auth is handled by OmniAuth in the middleware pipeline. It knows to redirect that request to the authorize URL configured by the auth provider.

So why POST? Why not GET? Unfortunately OmniAuth has an unresolved security issue: https://nvd.nist.gov/vuln/detail/CVE-2015-9284. To mitigate it, the sample uses omniauth-rails_csrf_protection. For that gem to protect against the attacks, it disables GET on the /auth/microsoft_graph_auth path. Follow that link for their explanation.

So the issue is likely that for whatever reason your browser is not sending the request as a POST. I can't reproduce your problem with Edge or Chrome. I can force the error by manually entering the URL in the address bar. Are you able to test with another browser to eliminate the browser?

Sorry, I missed the other question. It is when I click sign in at the very start. I never get to the point where I can enter credentials. The error is a standard rails routing error. It presents the routes table to show all the available routes.

No problem - GET is never going to work (by design) unless you remove the csrf_protection gem. That's going to potentially open you up to CSRF attacks though.

I agree that there seems to be something missing, I cannot seem to find or understand where it sends you back a login URL for microsoft. This sample does not work as is.

Closing this issue as it's not actionable. OmniAuth does abstract a lot of the auth work and redirects, which can make it a bit hard to follow. Here's how the sample works.

In route.rb, you configure OmniAuth routing like so:

# Add route for OmniAuth callback
match '/auth/:provider/callback', to: 'auth#callback', via: [:get, :post]

So anything going to /auth/:provider/callback will get sent to the callback method in auth_controller.rb. :provider is a placeholder and depends on which authentication provider is being used. In this sample, there's only one (microsoft_graph_auth), but in a production app you might have multiple (Facebook, Google, GitHub, etc.).

The sign in flow goes like this:

  • The sign-in links in the app go to /auth/microsoft_graph_auth
  • OmniAuth middleware intercepts and handles this request for you - there is no code that you have to write for it because OmniAuth's default OAuth handling of building the login URL works with only slight tweaking. How does that work? Well OAuth is a standard, and most OAuth providers follow that standard, so OmniAuth can get pretty close to working out of the box. All we need to do in our OmniAuth strategy is:

Here's the request as captured in the Network tab in Dev Tools:

Request URL: http://localhost:3000/auth/microsoft_graph_auth
Request Method: POST
Status Code: 302 Found
Remote Address: [::1]:3000
Referrer Policy: strict-origin-when-cross-origin
Cache-Control: no-cache
Content-Length: 352
Location: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=9d9349f2-57ed-49bd-92c1-3035d15bf579&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth%2Fmicrosoft_graph_auth%2Fcallback&response_type=code&scope=openid+profile+email+offline_access+user.read+calendars.read&state=98cf9a405028aa654558a93d65baf0c8273715b9e1183a4d