Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A minimal Sinatra OAuth 2.0 client and resource server.
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
You can’t perform that action at this time.