├── Gemfile ├── README.md ├── server.rb └── views ├── index.erb └── profile.erb /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'sinatra', '~> 2.0' 4 | gem 'rest-client', '~> 1.8' 5 | gem 'octokit', '~> 4.0' 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About this Quickstart Guide 2 | This is a quickstart guide to GitHub OAuth authentication using: 3 | - Ruby 4 | - The [Sinatra](http://sinatrarb.com) web framework 5 | - [Octokit](https://github.com/octokit/octokit.rb) - a Ruby client for the GitHub [REST API](https://github.com.) (makes interacting with GitHub data easier) 6 | 7 | To demonstrate the OAuth process, we're going to: 8 | 1) Set up a basic web server with Sinatra 9 | 2) Register a new OAuth Application on GitHub.com 10 | 3) Authenticate a user into our Sinatra app using GitHub's OAuth API 11 | 4) Use Octokit to retrieve this user's information from GitHub 12 | 13 | ## 1) Sinatra Webserver Setup 14 | First, go ahead and clone the starter code from the [OAuth Ruby Quickstart Guide](https://github.com/github/OAuth-Ruby-Quickstart) repository. 15 | 16 | This repository contains 3 files that we'll be working with: 17 | 18 | `server.rb` - Our Sinatra webserver 19 | 20 | `views/index.erb` - The webpage users will see before 'Signing in with GitHub' 21 | 22 | `views/profile.erb` - The webpage users will see _after_ authenticating with GitHub OAuth. This page will display their profile photo, along with other user information 23 | 24 | _P.S._([erb](https://ruby-doc.org/stdlib-2.5.1/libdoc/erb/rdoc/ERB.html) is a Ruby templating language which allows us to build HTML pages and pass in application data to be displayed on the page) 25 | 26 | 27 | To fire up our webapp, open a terminal and run the following command from the project directory. 28 | ``` 29 | $ ruby server.rb 30 | ``` 31 | 32 | You should see a message that **'Sinatra has taken the stage'** 33 | 34 | ![sinatra_on_stage](https://user-images.githubusercontent.com/3988879/40750699-5cb07faa-6425-11e8-829b-d2ffe7620871.png) 35 | 36 | We can now visit [http://localhost:4567](http://localhost:4567) to see our webpage. 37 | 38 | You should see something like this: 39 | 40 | ![localhost:4567](https://user-images.githubusercontent.com/3988879/40741163-fee7adca-6407-11e8-9465-9cbdb2072286.png) 41 | 42 | Our Sinatra app is up and running, but clicking that green **Sign In** button won't work yet. First, we need to register a new GitHub OAuth Application. 43 | 44 | 45 | ## 2) Register a new GitHub OAuth Application 46 | Head on over to your [Developer Settings](https://github.com/settings/developers) and register a new OAuth Application. 47 | 48 | ![screen shot 2018-05-25 at 4 23 36 pm](https://user-images.githubusercontent.com/3988879/40568485-0f8fb498-6038-11e8-8405-25142babaac8.png) 49 | 50 | 51 | You'll find the following fields: 52 | **Application name** - This must be unique among all GitHub OAuth Applications. 53 | **Homepage URL** - If you don't have a homepage for your application yet, just use your GitHub profile: `https://github.com/` (don't forget the `https://`). 54 | **Application description** - Explain to future users what this OAuth Application is used for. 55 | **Authorization callback URL** - GitHub needs to know where to redirect users after a successful OAuth authorization. For this app, the user should be redirected to the `/profile` view in our `server.rb` file. Since we're still working locally, we can actually use the localhost url from step #1: `http://localhost:4567/profile`. 56 | 57 | 58 | ## 3) Authenticate a User with GitHub’s OAuth API 59 | Now for the fun stuff. 60 | 61 | After registering your OAuth Application, you should see your Client ID and Client Secret. They'll look something like this: 62 | 63 | ![Client ID and Secret](https://user-images.githubusercontent.com/3988879/40741261-569e00d2-6408-11e8-96e4-3b454b5d7cbe.png) 64 | 65 | Copy the Client ID and Client Secret into their respective places in the `server.rb` file. 66 | 67 | Given the example above, the code would look like this: 68 | 69 | ![Client ID & Secret](https://user-images.githubusercontent.com/3988879/40741287-6c5267f6-6408-11e8-9d74-b5d1fe3f7232.png) 70 | 71 | With that set up, we're ready to authenticate a user with GitHub's OAuth API. 72 | 73 | With the Sinatra app running, go ahead and visit [localhost:4567](http://localhost:4567) again. This time, clicking the **Sign In with GitHub** button should take you to an authorization page: 74 | 75 | ![authorization](https://user-images.githubusercontent.com/3988879/40750372-4c5ae452-6424-11e8-8eda-67d03ca548fc.png) 76 | 77 | This is where users grant your OAuth Application permission to access their data. As you can see, we're only requesting public data right now. 78 | 79 | ##### What happened here? 80 | If you look at the URL in the address bar, you'll see your Client ID. On the previous page, the **Sign In with GitHub** button was in fact a link to an OAuth Authorization page, with your Client ID as a parameter. Sinatra built this link for us when we passed the `CLIENT_ID` into the index.erb template on line 24 of `server.rb`. Sinatra then interpolated our Client ID into the OAuth API link on line 15 of `index.erb`. 81 | 82 | Go ahead and Authorize the Application access to your GitHub account data, and you should be redirected to your callback page: `/profile`. 83 | 84 | ![success](https://user-images.githubusercontent.com/3988879/40751094-f98ceea2-6426-11e8-915a-c44e0f38cfb6.png) 85 | 86 | After a successful Application authorization, GitHub provides us with a temporary *authorization grant code* (see it in the URL up there?). We need to `POST` this code to the's `/login/oauth/access_token` endpoint to recieve an *access_token* The access token is what grants us access to the account data that we want to display. 87 | 88 | To simplify the process of `POST`ing the authorization grant code back to GitHub, we'll use the [rest-client](https://github.com/rest-client/rest-client) Ruby gem. 89 | 90 | First, let's update our `/profile` view to retrieve the temporary authorization grant code. We'll use Sinatra's built in webserver interface, [Rack](https://rack.github.io/) to grab the code from the session data. 91 | 92 | ``` ruby 93 | get '/profile' do 94 | # Retrieve temporary authorization grant code 95 | session_code = request.env['rack.request.query_hash']['code'] 96 | 97 | erb :profile 98 | end 99 | ``` 100 | 101 | Now that we have the authorization grant code, let's use `rest-client` to `POST` it back to GitHub along with our `CLIENT_ID` and `CLIENT_SECRET` in exchange for our `access_token`. 102 | 103 | The `https://github.com/login/oauth/access_token` endpoint expects the following parameters: 104 | 105 | Name | Type | Description 106 | -- | -- | -- 107 | `client_id` | `string` | __Required.__ Your GitHub OAuth Application Client ID 108 | `client_secret` | `string` | __Required.__ Your GitHub OAuth Application Client Secret 109 | `code` | `string` | __Required.__ The authorization grant code returned after Application authorization 110 | 111 | 112 | Let's also use the request header `:accept` to let the API know that we'd like a `JSON` formatted response. 113 | 114 | ``` ruby 115 | get '/profile' do 116 | # Retrieve temporary authorization grant code 117 | session_code = request.env['rack.request.query_hash']['code'] 118 | 119 | # POST Auth Grant Code + CLIENT_ID/SECRECT in exchange for our access_token 120 | response = RestClient.post('https://github.com/login/oauth/access_token', 121 | # POST payload 122 | {:client_id => CLIENT_ID 123 | :client_secret => CLIENT_SECRET 124 | :code => session_code}, 125 | # Request header for JSON response 126 | :accept => :json) 127 | 128 | erb :profile 129 | end 130 | ``` 131 | 132 | The API's response to our `POST` request will include the access token in a field called `access_token` in the `response` variable. Now we just need to parse out the access token. 133 | 134 | ``` ruby 135 | #Parse access_token from JSON response 136 | access_token = JSON.parse(response)['access_token'] 137 | ``` 138 | 139 | ## 4) Use Octokit to Access User Data 140 | 141 | Now that we finally have our access token, we can start acessing user data via the GitHub API. Instead of manually calling / handling our own REST request / responses to the API, we can save some time and effort by using GitHub's official Ruby library, [Octokit](http://octokit.github.io/octokit.rb/). 142 | 143 | First, we need initialize the Octokit client by passing it our access token `Octokit::Client.new(:access_token => access_token)`. After that, the user data associated with our `access_token` is available as `client.user`. We can now access any of the [user data properties](https://developer.github.com/v3/users/#get-the-authenticated-user) available from the v3 REST API. 144 | 145 | Here is the entire function, with the call to Octokit: 146 | 147 | ``` ruby 148 | get '/profile' do 149 | # Retrieve temporary authorization grant code 150 | session_code = request.env['rack.request.query_hash']['code'] 151 | 152 | # POST Auth Grant Code + CLIENT_ID/SECRECT in exchange for our access_token 153 | response = RestClient.post('https://github.com/login/oauth/access_token', 154 | # POST payload 155 | {:client_id => CLIENT_ID, 156 | :client_secret => CLIENT_SECRET, 157 | :code => session_code}, 158 | # Request header for JSON response 159 | :accept => :json) 160 | 161 | # Parse access_token from JSON response 162 | access_token = JSON.parse(response)['access_token'] 163 | 164 | # Initialize Octokit client with user access_token 165 | client = Octokit::Client.new(:access_token => access_token) 166 | 167 | # Create user object for less typing 168 | user = client.user 169 | 170 | # Access user data 171 | profile_data = {:user_photo_url => user.avatar_url, 172 | :user_login => user.login, 173 | :user_name => user.name, 174 | :user_id => user.id } 175 | 176 | # Render profile page, passing in user profile data to be displayed 177 | erb :profile, :locals => profile_data 178 | end 179 | ``` 180 | 181 | All of the template [locals](http://sinatrarb.com/intro.html) we'd like to use are packaged up in `profile_data` and will be availble on the `profile.erb` page. 182 | 183 | Now we just need to add in the erb partials in `profile.erb` to display the data. 184 | 185 | ``` html 186 | 187 |
188 |
189 | 190 |

191 | Hey, @<%= user_login %>! 192 |

193 |

194 | You are GitHub user #<%= user_id %> 195 |

196 |

197 | Click here to see all of 198 |
your public profile data 199 |

200 |
201 |
202 | 203 | ``` 204 | 205 | The final result should look something like this: 206 | complete 207 | -------------------------------------------------------------------------------- /server.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' # Our handy lightweight webserver 2 | require 'rest-client' # Used for authentication with the GitHub OAuth API 3 | require 'json' # Used to parse the JSON response from the GitHub OAuth API 4 | require 'octokit' # Used to retrieve user data after authentication 5 | 6 | =begin 7 | [ !IMPORTANT! ] 8 | In production, the CLIENT_ID & CLIENT_SECRET (below) should never be 9 | hard-coded or exposed. Instead, you should export environment 10 | variables for these values. NEVER check your CLIENT_ID or CLIENT_SECRET 11 | into a git repository that is or may become public. 12 | 13 | In production, these values should be accessed as environement variables 14 | like this: CLIENT_ID = ENV['CLIENT_ID'] 15 | =end 16 | 17 | CLIENT_ID = ' {:client_id => CLIENT_ID} 25 | end 26 | 27 | 28 | # This is our 'logged-in' view, displaying profile.erb after the user has 29 | # authenticated with GitHub (this is the 'callback URL' we entered when 30 | # registering our OAUth Application on GitHub.com). 31 | get '/profile' do 32 | erb :profile 33 | end -------------------------------------------------------------------------------- /views/index.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 |
11 |
12 |

Hey there,

13 |

We're going to sign in with the GitHub OAuth API. Ready?

14 |

15 | Sign In with GitHub 16 |

17 |
18 |

19 | If that link doesn't work, remember to add in your Client ID! 20 |

21 |
22 |
23 | 24 | -------------------------------------------------------------------------------- /views/profile.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 20 | 21 | 22 |
23 |
24 | Success! You've Authenticated an OAuth Application with GitHub, and recieved a callback redirect to /profile. 25 |
26 |
27 | 28 | --------------------------------------------------------------------------------