omniauth-jwt-0.0.2/0000755000000000000000000000000013267132077012645 5ustar rootrootomniauth-jwt-0.0.2/lib/0000755000000000000000000000000013267132077013413 5ustar rootrootomniauth-jwt-0.0.2/lib/omniauth/0000755000000000000000000000000013267132077015237 5ustar rootrootomniauth-jwt-0.0.2/lib/omniauth/jwt/0000755000000000000000000000000013267132077016043 5ustar rootrootomniauth-jwt-0.0.2/lib/omniauth/jwt/version.rb0000664000000000000000000000007513267132077020061 0ustar rootrootmodule Omniauth module JWT VERSION = "0.0.2" end end omniauth-jwt-0.0.2/lib/omniauth/jwt.rb0000664000000000000000000000010013267132077016361 0ustar rootrootrequire "omniauth/jwt/version" require "omniauth/strategies/jwt"omniauth-jwt-0.0.2/lib/omniauth/strategies/0000755000000000000000000000000013267132077017411 5ustar rootrootomniauth-jwt-0.0.2/lib/omniauth/strategies/jwt.rb0000600000000000000000000000302013267132077020525 0ustar rootrootrequire 'omniauth' require 'jwt' module OmniAuth module Strategies class JWT class ClaimInvalid < StandardError; end include OmniAuth::Strategy args [:secret] option :secret, nil option :algorithm, 'HS256' option :uid_claim, 'email' option :required_claims, %w(name email) option :info_map, {"name" => "name", "email" => "email"} option :auth_url, nil option :valid_within, nil def request_phase redirect options.auth_url end def decoded @decoded ||= ::JWT.decode(request.params['jwt'], options.secret, options.algorithm) (options.required_claims || []).each do |field| raise ClaimInvalid.new("Missing required '#{field}' claim.") if !@decoded.key?(field.to_s) end raise ClaimInvalid.new("Missing required 'iat' claim.") if options.valid_within && !@decoded["iat"] raise ClaimInvalid.new("'iat' timestamp claim is too skewed from present.") if options.valid_within && (Time.now.to_i - @decoded["iat"]).abs > options.valid_within @decoded end def callback_phase super rescue ClaimInvalid => e fail! :claim_invalid, e end uid{ decoded[options.uid_claim] } extra do {:raw_info => decoded} end info do options.info_map.inject({}) do |h,(k,v)| h[k.to_s] = decoded[v.to_s] h end end end class Jwt < JWT; end end endomniauth-jwt-0.0.2/.gitignore0000664000000000000000000000023213267132077014634 0ustar rootroot*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp omniauth-jwt-0.0.2/omniauth-jwt.gemspec0000664000000000000000000000224413267132077016644 0ustar rootroot# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'omniauth/jwt/version' Gem::Specification.new do |spec| spec.name = "omniauth-jwt" spec.version = Omniauth::JWT::VERSION spec.authors = ["Michael Bleigh"] spec.email = ["mbleigh@mbleigh.com"] spec.description = %q{An OmniAuth strategy to accept JWT-based single sign-on.} spec.summary = %q{An OmniAuth strategy to accept JWT-based single sign-on.} spec.homepage = "http://github.com/mbleigh/omniauth-jwt" spec.license = "MIT" spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency "rake" spec.add_development_dependency "rspec" spec.add_development_dependency "guard" spec.add_development_dependency "guard-rspec" spec.add_development_dependency "rack-test" spec.add_dependency "jwt" spec.add_dependency "omniauth", "~> 1.1" end omniauth-jwt-0.0.2/README.md0000664000000000000000000000621313267132077014130 0ustar rootroot# OmniAuth::JWT [![Build Status](https://travis-ci.org/mbleigh/omniauth-jwt.png)](https://travis-ci.org/mbleigh/omniauth-jwt) [JSON Web Token](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html) (JWT) is a simple way to send verified information between two parties online. This can be useful as a mechanism for providing Single Sign-On (SSO) to an application by allowing an authentication server to send a validated claim and log the user in. This is how [Zendesk does SSO](https://support.zendesk.com/entries/23675367-Setting-up-single-sign-on-with-JWT-JSON-Web-Token-), for example. OmniAuth::JWT provides a clean, simple wrapper on top of JWT so that you can easily implement this kind of SSO either between your own applications or allow third parties to delegate authentication. ## Installation Add this line to your application's Gemfile: gem 'omniauth-jwt' And then execute: $ bundle Or install it yourself as: $ gem install omniauth-jwt ## Usage You use OmniAuth::JWT just like you do any other OmniAuth strategy: ```ruby use OmniAuth::JWT, 'SHAREDSECRET', auth_url: 'http://example.com/login' ``` The first parameter is the shared secret that will be used by the external authenticator to verify that. You must also specify the `auth_url` option to tell the strategy where to redirect to log in. Other available options are: * **algorithm:** the algorithm to use to decode the JWT token. This is `HS256` by default but can be set to anything supported by [ruby-jwt](https://github.com/progrium/ruby-jwt) * **uid_claim:** this determines which claim will be used to uniquely identify the user. Defaults to `email` * **required_claims:** array of claims that are required to make this a valid authentication call. Defaults to `['name', 'email']` * **info_map:** array mapping claim values to info hash values. Defaults to mapping `name` and `email` to the same in the info hash. * **valid_within:** integer of how many seconds of time skew you will allow. Defaults to `nil`. If this is set, the `iat` claim becomes required and must be within the specified number of seconds of the current time. This helps to prevent replay attacks. ### Authentication Process When you authenticate through `omniauth-jwt` you can send users to `/auth/jwt` and it will redirect them to the URL specified in the `auth_url` option. From there, the provider must generate a JWT and send it to the `/auth/jwt/callback` URL as a "jwt" parameter: /auth/jwt/callback?jwt=ENCODEDJWTGOESHERE An example of how to do that in Sinatra: ```ruby require 'jwt' get '/login/sso/other-app' do # assuming the user is already logged in and this is available as current_user claims = { id: current_user.id, name: current_user.name, email: current_user.email, iat: Time.now.to_i } payload = JWT.encode(claims, ENV['SSO_SECRET']) redirect "http://other-app.com/auth/jwt/callback?jwt=#{payload}" end ``` ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request omniauth-jwt-0.0.2/spec/0000755000000000000000000000000013267132077013577 5ustar rootrootomniauth-jwt-0.0.2/spec/spec_helper.rb0000664000000000000000000000161213267132077016417 0ustar rootroot$:.unshift File.dirname(__FILE__) + "/../lib" require 'rack/test' require 'omniauth/jwt' OmniAuth.config.logger = Logger.new('/dev/null') # This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # Require this file using `require "spec_helper"` to ensure that it is only # loaded once. # # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| config.treat_symbols_as_metadata_keys_with_true_values = true config.run_all_when_everything_filtered = true config.filter_run :focus include Rack::Test::Methods # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 config.order = 'random' end omniauth-jwt-0.0.2/spec/lib/0000755000000000000000000000000013267132077014345 5ustar rootrootomniauth-jwt-0.0.2/spec/lib/omniauth/0000755000000000000000000000000013267132077016171 5ustar rootrootomniauth-jwt-0.0.2/spec/lib/omniauth/strategies/0000755000000000000000000000000013267132077020343 5ustar rootrootomniauth-jwt-0.0.2/spec/lib/omniauth/strategies/jwt_spec.rb0000600000000000000000000000456713267132077022512 0ustar rootrootrequire 'spec_helper' describe OmniAuth::Strategies::JWT do let(:response_json){ MultiJson.load(last_response.body) } let(:args){ ['imasecret', {auth_url: 'http://example.com/login'}] } let(:app){ the_args = args Rack::Builder.new do |b| b.use Rack::Session::Cookie, secret: 'sekrit' b.use OmniAuth::Strategies::JWT, *the_args b.run lambda{|env| [200, {}, [(env['omniauth.auth'] || {}).to_json]]} end } context 'request phase' do it 'should redirect to the configured login url' do get '/auth/jwt' expect(last_response.status).to eq(302) expect(last_response.headers['Location']).to eq('http://example.com/login') end end context 'callback phase' do it 'should decode the response' do encoded = JWT.encode({name: 'Bob', email: 'steve@example.com'}, 'imasecret') get '/auth/jwt/callback?jwt=' + encoded expect(response_json["info"]["email"]).to eq("steve@example.com") end it 'should not work without required fields' do encoded = JWT.encode({name: 'Steve'}, 'imasecret') get '/auth/jwt/callback?jwt=' + encoded expect(last_response.status).to eq(302) end it 'should assign the uid' do encoded = JWT.encode({name: 'Steve', email: 'dude@awesome.com'}, 'imasecret') get '/auth/jwt/callback?jwt=' + encoded expect(response_json["uid"]).to eq('dude@awesome.com') end context 'with a :valid_within option set' do let(:args){ ['imasecret', {auth_url: 'http://example.com/login', valid_within: 300}] } it 'should work if the iat key is within the time window' do encoded = JWT.encode({name: 'Ted', email: 'ted@example.com', iat: Time.now.to_i}, 'imasecret') get '/auth/jwt/callback?jwt=' + encoded expect(last_response.status).to eq(200) end it 'should not work if the iat key is outside the time window' do encoded = JWT.encode({name: 'Ted', email: 'ted@example.com', iat: Time.now.to_i + 500}, 'imasecret') get '/auth/jwt/callback?jwt=' + encoded expect(last_response.status).to eq(302) end it 'should not work if the iat key is missing' do encoded = JWT.encode({name: 'Ted', email: 'ted@example.com'}, 'imasecret') get '/auth/jwt/callback?jwt=' + encoded expect(last_response.status).to eq(302) end end end endomniauth-jwt-0.0.2/Rakefile0000664000000000000000000000015513267132077014315 0ustar rootrootrequire "bundler/gem_tasks" require "rspec/core/rake_task" RSpec::Core::RakeTask.new task :default => :specomniauth-jwt-0.0.2/LICENSE.txt0000664000000000000000000000205713267132077014476 0ustar rootrootCopyright (c) 2013 Michael Bleigh MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. omniauth-jwt-0.0.2/Gemfile0000664000000000000000000000014113267132077014136 0ustar rootrootsource 'https://rubygems.org' # Specify your gem's dependencies in omniauth-jwt.gemspec gemspec omniauth-jwt-0.0.2/.travis.yml0000664000000000000000000000007713267132077014764 0ustar rootrootlanguage: ruby rvm: - 2.0.0 - 1.9.3 - jruby-19mode - rbx-19modeomniauth-jwt-0.0.2/.rspec0000664000000000000000000000003213267132077013757 0ustar rootroot--color --format progress omniauth-jwt-0.0.2/Guardfile0000644000000000000000000000035413267132077014474 0ustar rootroot# A sample Guardfile # More info at https://github.com/guard/guard#readme guard :rspec do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } watch('spec/spec_helper.rb') { "spec" } end