-
-
Save elmer/375e221736324eaef76ca3a55b509d21 to your computer and use it in GitHub Desktop.
A minimal Sinatra OAuth 2.0 client and resource server.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'sinatra' | |
require 'open-uri' | |
require 'json' | |
# This application is the bare minimum to authorize with OAuth 2.0 | |
# using the authorization grant scheme. No error handling included. | |
# The application is both a client and a resource server. | |
# Start it by using 'ruby <file>' and navigate to http://localhost:4567 | |
# | |
# The application also needs the oauth-server written in Java. | |
# See https://github.com/comoyo/oauth-server | |
# Start it with 'mvn jetty:run' for now. | |
configure do | |
set client_id: "123456789" | |
set client_secret: "kakekake" | |
set oauth_host: "http://localhost:8080/api" | |
end | |
# Global variables. So much ugly... | |
@@access_token = "" | |
@@redirect_uri = 'http://localhost:4567/oauth/callback' | |
# Client paths | |
# Root path. Begin here. | |
get '/' do | |
"<a href='/oauth/redirect'>Authorize this app, motherfucker.</a>" | |
end | |
# The button on the main page takes the user here. | |
# Here, we build a redirect uri to the authorization server with | |
# the client id, state, redirect_uri to our callback and more. | |
get '/oauth/redirect' do | |
# Set some easy to read variables. | |
response_type = "code" | |
state = "kake" | |
redirect_uri = @@redirect_uri | |
# Build redirect uri | |
uri = settings.oauth_host + "/oauth/auth" | |
uri += "?client_id=#{settings.client_id}" | |
uri += "&response_type=#{response_type}" | |
uri += "&state=#{state}" | |
uri += "&redirect_uri=#{redirect_uri}" | |
# Send a 302 Temporary Redirect to the user-agent. | |
# This will redirect the user to the Authorization server. | |
redirect uri | |
end | |
# The user-agent is redirected here by the oauth-server after the app was given authorization. | |
# Here, we extract the authorization code and do an internal request to the token endpoint | |
# to retrieve the access token and optional refresh token. | |
get '/oauth/callback' do | |
# Set some easy to read variables. | |
code = params[:code] | |
grant_type = "authorization_code" | |
redirect_uri = @@redirect_uri | |
# Build request URI | |
uri = URI(settings.oauth_host + "/oauth/token") | |
uri.query = "code=#{code}&grant_type=#{grant_type}&redirect_uri=#{redirect_uri}" | |
# Open an HTTP connection to the authorization server using basic auth. | |
open(uri, :http_basic_authentication=>[settings.client_id, settings.client_secret]) do |http| | |
@respons = JSON.parse(http.read) | |
end | |
# Extract the access token from the json response. | |
@@access_token = @respons["access_token"] | |
# Redirect the user to another url to hide the ugly authcode url. | |
redirect to('/oauth/finish') | |
end | |
# The user has now authorized our app, and the client has procured an access token. | |
# Here, we just present the user with a simple message and a prompt to fetch a protected resource. | |
# In reality, the client doesn't need user action to use the access token during the lifetime of the token. | |
get '/oauth/finish' do | |
path = "/rs/protected?access_token=#{@@access_token}&client_id=#{settings.client_id}" | |
"Authorized.<br><a href='#{path}'>Fetch protected resource</a>" | |
end | |
# Resource Server paths. | |
# This is our resource server path. It is protected from everyone that do not have an access token. | |
# This can usually be a users watched movies, the phone number of the user or similar. | |
# The resource server MUST validate the token and verify that the audience field matches the supplied client_id. | |
get '/rs/protected' do | |
access_token = params[:access_token] | |
client_id = params[:client_id] | |
if valid_token?(access_token, client_id) | |
return "Congratulations! You have access to this token." | |
end | |
"You do not have access to this resource." | |
end | |
# A convenience method to validate a token by requesting token information from the authorization server. | |
def valid_token?(token, client_id) | |
# Build request URI | |
uri = URI(settings.oauth_host + "/oauth/tokeninfo") | |
uri.query = "access_token=#{token}" | |
puts uri.to_s | |
# Open an HTTP connection to uri | |
open(uri) do |http| | |
@respons = JSON.parse(http.read) | |
end | |
# Only return true if the server returns successfully, and the audience field matches the client id. | |
if not @respons["audience"].nil? && @respons["audience"] == client_id | |
return true | |
end | |
return false | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment