PhoenixOAuth2

HomePage | RecentChanges | Preferences

OAuth2 with Phoenix

OAuth2

Note: This is a blog post in progress, saved here in case (a) something happens to my laptop or (b) I never finish it, and this is useful to someone.

--- layout: post title: "Adding OAuth2 to a Phoenix app" date: 2015-07-19 14:37:00 tags: elixir phoenix oauth2 ---

NOTE: Example app is in... my_app_717537

Follow mix-new-to-heroku except add access_token and refresh_token to the user model

The very first thing we need is a link for the user to click. web/templates/page/index.html.eex

  <a href="/auth"><h2>Sign in with Fitbit</h2></a>

  But of course it's an error, there's nothing there.

Phoenix.Router.NoRouteError? at GET /auth no route found for GET /auth ([MyApp 717537]?.Router)

  We need a route.  Add the /auth scope in web/router.ex

  scope "/auth", [MyApp 717537]? do
    pipe_through :browser
    get "/", AuthController?, :index
  end

This says that when a request comes in for /auth, we should go through the :browser pipeline (it's further up the same file) and then start looking for a match. If we see a GET for the 'root' of "/auth" then go to the :index action of the Auth Controller.

Now if we go back and click the sign in link, we get

UndefinedFunctionError? at GET /auth undefined function: [MyApp 717537]?.AuthController?.init/1 (module [MyApp 717537]?.AuthController? is not available)

I didn't see a way to generate a controller on http://www.phoenixframework.org/v0.10.0/docs/mix-tasks or with with $ mix help | grep -i phoenix

But we can copy the page_controller.ex and modify it.

  Add the web/controllers/auth_controller.ex

(page_controller.ex) defmodule [MyApp 717537]?.PageController? do

  use [MyApp 717537]?.Web, :controller

  def index(conn, _params) do
    render conn, "index.html"
  end
end

s/PageController?/AuthController?/ and in this case we want to redirect to Fitbit per https://wiki.fitbit.com/display/API/OAuth+2.0

defmodule [MyApp 717537]?.AuthController? do

  use [MyApp 717537]?.Web, :controller

  def index(conn, _params) do
    redirect conn, external: "https://www.fitbit.com/oauth2/authorize"
  end
end

And now go back and click, and you should land on Fitbit's site with an error because we haven't sent them all the required info.

Now it's time to incorporate the OAuth2 library.

Review the instructions on https://github.com/scrogson/oauth2

Add oauth2 as a dependency and to the list of applications

- :phoenix_ecto, :postgrex]] + :phoenix_ecto, :postgrex, :oauth2]]

- {:cowboy, "~> 1.0"}] + {:cowboy, "~> 1.0"}, + {:oauth2, "~> 0.1.1"}]

mix deps.get

commit mix.exs and mix.lock

We're going to use the "Authorization Code [Grant] Flow" described in the OAuth2 Readme and also the Fitbit docs. (Not the Implicit Grant Flow.) There is a small typo I reported. https://community.fitbit.com/t5/Web-API/Typo-in-OAuth2-docs/m-p/871896#U871896

https://github.com/scrogson/oauth2#authorization-code-flow-authcode-strategy says we should initialize a client. where does that go? Oh, further down, it talks about writing your own strategy. that must be what we need.

He links to an example, let's see where things go. https://github.com/scrogson/oauth2_example

Okay, based on this we'll have web/oauth/fitbit.ex

paste in the GitHub example (which we can do because of the MIT license) but should not do unless we understand everything in it

What's with `alias OAuth2.Strategy.AuthCode?` ? http://elixir-lang.org/getting-started/alias-require-and-import.html#alias

ah, this is the same as `alias OAuth2.Strategy.AuthCode?, as: AuthCode?` so later we can just type `AuthCode?` instead of the whole long thing.

What's with `strategy: __MODULE__,` ?

It must substitute in this module's name?

What's with the \\ in `def authorize_url!(params \\ []) do` ?

See bottom of http://elixir-lang.org/crash-course.html#identifying-functions -- it's a default value if nothing is passed in

We've said `use OAuth2.Strategy` what does that mean exactly?

How are we calling functions on OAuth2.Client without importing it or whatever? Suspicion that everything in deps/ is available all the time...

Note that there are some environment variables we'll need to set and some urls we'll need to change.


Next up is sorting out OAuth2 with Phoenix

Clone https://github.com/scrogson/oauth2_example locally

git clone git@github.com:scrogson/oauth2_example.git phoenix_oauth2_example

Register an app at GitHub https://github.com/settings/applications/new

For the Authorization callback url, enter http://lvh.me:4000/app/callback

I found this with mix phoenix.routes

output of mix phoenix.server included...

16:09:16.872 [error] Could not start watcher "node_modules/brunch/bin/brunch", executable does not exist
...but it started, sans stylesheets (again).

The fix for that is `npm install` in the project directory, to install nodejs dependencies.

I forked the repo and submitted a pull request to update the readme https://github.com/scrogson/oauth2_example/pull/7

I might need an OAuth primer. Even when I revoke the app's access from my GitHub user account, or revoke all user tokens from the app configuration, going to http://lvh.me:4000 still shows me logged in until I delete the cookie for that domain. Then it switches back to "Log in with GitHub". Revoking all user tokens *says* it will force them back through the authorization flow. Mystery solved: cookie session storage, the values are being sent back by the browser, not fetched from the api.

Onward! I already forked to patch the docs, so I created a new 'fitbit' branch. Let's switch over from GitHub to Fitbit and see what needs to change.


I'm getting protocol String.Chars not implemented from `Logger.info token` trying to log the value of a variable of type OAuth2.AccessToken? (from https://github.com/scrogson/oauth2)

How does Phoenix dump the 'contents' of that variable on the error page? That's more or less what I want... Solution: IO.inspect in the pipe or IO.inspect [something] elsewhere

ElixirDebugging


HomePage | RecentChanges | Preferences
This page is read-only | View other revisions
Last edited September 5, 2015 10:39 am by 198.98.94.133 (diff)
Search: