theworldbright 2015-10-03 17:47:23 -07:00
parent 1dcefdb998
commit fd467cd42b
13 changed files with 307 additions and 21 deletions

1
.gitignore vendored
View file

@ -21,6 +21,7 @@ config/database.yml
.rvmrc_custom
.rvmrc.local
config/oidc_key.pem
config/jwks/
# Mailing list stuff
config/email_offset

View file

@ -32,7 +32,7 @@ module Api
private
def http_error_page_as_json(e)
render json: { error: :invalid_request, error_description: e.message}, status: 400
render json: {error: :invalid_request, error_description: e.message}, status: 400
end
def validation_fail_as_json(e)

View file

@ -25,7 +25,7 @@ module Api
request_object_signing_alg_values_supported: %i(HS256 HS384 HS512),
subject_types_supported: %w(public pairwise),
id_token_signing_alg_values_supported: %i(RS256),
token_endpoint_auth_methods_supported: %w(client_secret_basic client_secret_post),
token_endpoint_auth_methods_supported: %w(client_secret_basic client_secret_post private_key_jwt),
claims_supported: %w(sub nickname profile picture)
)
end

View file

@ -0,0 +1,64 @@
module Api
module OpenidConnect
class TokenEndpointController < ApplicationController
def create
req = Rack::Request.new(request.env)
if req["client_assertion_type"] == "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
handle_jwt_bearer(req)
end
self.status, self.response.headers, self.response_body = Api::OpenidConnect::TokenEndpoint.new.call(request.env)
nil
end
private
def handle_jwt_bearer(req)
jwt_string = req["client_assertion"]
jwt = JSON::JWT.decode jwt_string, :skip_verification
o_auth_app = Api::OpenidConnect::OAuthApplication.find_by(client_id: jwt["iss"])
raise Rack::OAuth2::Server::Authorize::BadRequest(:invalid_request) unless o_auth_app
public_key = fetch_public_key(o_auth_app, jwt)
JSON::JWT.decode(jwt_string, JSON::JWK.new(public_key).to_key)
req.update_param("client_id", o_auth_app.client_id)
req.update_param("client_secret", o_auth_app.client_secret)
end
def fetch_public_key(o_auth_app, jwt)
jwks_file_path = File.join(Rails.root, "config", "jwks", o_auth_app.jwks_file)
public_key = fetch_public_key_from_json(File.read(jwks_file_path), jwt)
if public_key.empty? && o_auth_app.jwks_uri
uri = URI.parse(o_auth_app.jwks_uri)
response = Net::HTTP.get_response(uri)
File.write jwks_file_path, response.body
public_key = fetch_public_key_from_json(response.body, jwt)
end
raise Rack::OAuth2::Server::Authorize::BadRequest(:unauthorized_client) if public_key.empty?
public_key
end
def fetch_public_key_from_json(string, jwt)
json = JSON.parse(string)
keys = json["keys"]
public_key = get_key_from_kid(keys, jwt.header["kid"])
public_key
end
def get_key_from_kid(keys, kid)
keys.each do |key|
return key if key.has_value?(kid)
end
end
rescue_from Rack::OAuth2::Server::Authorize::BadRequest, JSON::JWT::InvalidFormat do |e|
logger.info e.backtrace[0, 10].join("\n")
render json: {error: :invalid_request, error_description: e.message, status: e.status}
end
rescue_from JSON::JWT::InvalidFormat do |e|
render json: {error: :invalid_request, error_description: e.message, status: 400}
end
rescue_from JSON::JWT::VerificationFailed do |e|
render json: {error: :invalid_grant, error_description: e.message, status: 400}
end
end
end
end

View file

@ -1,3 +1,5 @@
require "digest"
module Api
module OpenidConnect
class OAuthApplication < ActiveRecord::Base
@ -68,7 +70,7 @@ module Api
def supported_metadata
%i(client_name response_types grant_types application_type
contacts logo_uri client_uri policy_uri tos_uri redirect_uris
sector_identifier_uri subject_type)
sector_identifier_uri subject_type token_endpoint_auth_method jwks jwks_uri)
end
def registrar_attributes(registrar)
@ -77,11 +79,30 @@ module Api
next unless value
if key == :subject_type
attr[:ppid] = (value == "pairwise")
elsif key == :jwks_uri
uri = URI.parse(value)
response = Net::HTTP.get_response(uri)
file_name = create_file_path(response.body)
attr[:jwks_file] = file_name + ".json"
attr[:jwks_uri] = value
elsif key == :jwks
file_name = create_file_path(value.to_json)
attr[:jwks_file] = file_name + ".json"
else
attr[key] = value
end
end
end
def create_file_path(content)
file_name = Base64.urlsafe_encode64(Digest::SHA256.base64digest(content))
directory_name = File.join(Rails.root, "config", "jwks")
Dir.mkdir(directory_name) unless File.exist?(directory_name)
jwk_file_path = File.join(Rails.root, "config", "jwks", file_name + ".json")
File.write jwk_file_path, content
File.chmod(0600, jwk_file_path)
file_name
end
end
end
end

View file

@ -242,7 +242,7 @@ Diaspora::Application.routes.draw do
resources :clients, only: :create
get "clients/find", to: "clients#find"
post "access_tokens", to: proc {|env| Api::OpenidConnect::TokenEndpoint.new.call(env) }
post "access_tokens", to: "token_endpoint#create"
# Authorization Servers MUST support the use of the HTTP GET and POST methods at the Authorization Endpoint
# See http://openid.net/specs/openid-connect-core-1_0.html#AuthResponseValidation

View file

@ -16,6 +16,10 @@ class CreateOAuthApplications < ActiveRecord::Migration
t.string :policy_uri
t.string :tos_uri
t.string :sector_identifier_uri
t.string :token_endpoint_auth_method
t.string :jwks_uri
t.string :jwks_file
t.boolean :ppid, default: false
t.timestamps null: false

View file

@ -273,23 +273,26 @@ ActiveRecord::Schema.define(version: 20150828132451) do
add_index "o_auth_access_tokens", ["token"], name: "index_o_auth_access_tokens_on_token", unique: true, length: {"token"=>191}, using: :btree
create_table "o_auth_applications", force: :cascade do |t|
t.integer "user_id", limit: 4
t.string "client_id", limit: 255
t.string "client_secret", limit: 255
t.string "client_name", limit: 255
t.string "redirect_uris", limit: 255
t.string "response_types", limit: 255
t.string "grant_types", limit: 255
t.string "application_type", limit: 255, default: "web"
t.string "contacts", limit: 255
t.string "logo_uri", limit: 255
t.string "client_uri", limit: 255
t.string "policy_uri", limit: 255
t.string "tos_uri", limit: 255
t.string "sector_identifier_uri", limit: 255
t.boolean "ppid", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id", limit: 4
t.string "client_id", limit: 255
t.string "client_secret", limit: 255
t.string "client_name", limit: 255
t.string "redirect_uris", limit: 255
t.string "response_types", limit: 255
t.string "grant_types", limit: 255
t.string "application_type", limit: 255, default: "web"
t.string "contacts", limit: 255
t.string "logo_uri", limit: 255
t.string "client_uri", limit: 255
t.string "policy_uri", limit: 255
t.string "tos_uri", limit: 255
t.string "sector_identifier_uri", limit: 255
t.string "token_endpoint_auth_method", limit: 255
t.string "jwks_uri", limit: 255
t.string "jwks_file", limit: 255
t.boolean "ppid", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "o_auth_applications", ["client_id"], name: "index_o_auth_applications_on_client_id", unique: true, length: {"client_id"=>191}, using: :btree

View file

@ -19,6 +19,87 @@ describe Api::OpenidConnect::ClientsController, type: :controller do
end
end
context "when valid parameters with jwks is passed" do
it "should return a client id" do
stub_request(:get, "http://example.com/uris")
.with(headers: {"Accept" => "*/*", "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"Host" => "example.com", "User-Agent" => "Ruby"})
.to_return(status: 200, body: "[\"http://localhost\"]", headers: {})
post :create, redirect_uris: ["http://localhost"], client_name: "diaspora client",
response_types: [], grant_types: [], application_type: "web", contacts: [],
logo_uri: "http://example.com/logo.png", client_uri: "http://example.com/client",
policy_uri: "http://example.com/policy", tos_uri: "http://example.com/tos",
sector_identifier_uri: "http://example.com/uris", subject_type: "pairwise",
token_endpoint_auth_method: "private_key_jwt",
"jwks": {
"keys":
[
{
"use": "enc",
"e": "AQAB",
"d": "-lTBWkI-----lvCO6tuiDsR4qgJnUwnndQFwEI_4mLmD3iNWXrc8N--5Cjq55eLtuJjtvuQ",
"n": "--zYRQNDvIVsBDLQQIgrbctuGqj6lrXb31Jj3JIEYqH_4h5X9d0Q",
"q": "1q-r----pFtyTz_JksYYaotc_Z3Zy-Szw6a39IDbuYGy1qL-15oQuc",
"p": "-BfRjdgYouy4c6xAnGDgSMTip1YnPRyvbMaoYT9E_tEcBW5wOeoc",
"kid": "a0",
"kty": "RSA"
},
{"use": "sig",
"e": "AQAB",
"d": "--x-gW---LRPowKrdvTuTo2p--HMI0pIEeFs7H_u5OW3jihjvoFClGPynHQhgWmQzlQRvWRXh6FhDVqFeGQ",
"n": "---TyeadDqQPWgbqX69UzcGq5irhzN8cpZ_JaTk3Y_uV6owanTZLVvCgdjaAnMYeZhb0KFw",
"q": "5E5XKK5njT--Hx3nF5sne5fleVfU-sZy6Za4B2U75PcE62oZgCPauOTAEm9Xuvrt5aMMovyzR8ecJZhm9bw7naU",
"p": "-BUGA-",
"kid": "a1",
"kty": "RSA"},
{
"use": "sig",
"crv": "P-256",
"kty": "EC",
"y": "Yg4IRzHBMIsuQK2Oz0Uukp1aNDnpdoyk6QBMtmfGHQQ",
"x": "L0WUeVlc9r6YJd6ie9duvOU1RHwxSkJKA37IK9B4Bpc",
"kid": "a2"
},
{
"use": "enc",
"crv": "P-256",
"kty": "EC",
"y": "E6E6g5_ziIZvfdAoACctnwOhuQYMvQzA259aftPn59M",
"x": "Yu8_BQE2L0f1MqnK0GumZOaj_77Tx70-LoudyRUnLM4",
"kid": "a3"
}
]
}
client_json = JSON.parse(response.body)
expect(client_json["client_id"].length).to eq(32)
expect(client_json["ppid"]).to eq(true)
end
end
context "when valid parameters with jwks_uri is passed" do
it "should return a client id" do
stub_request(:get, "http://example.com/uris")
.with(headers: {:Accept => "*/*", :"Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"Host" => "example.com", :"User-Agent" => "Ruby"})
.to_return(status: 200, body: "[\"http://localhost\"]", headers: {})
stub_request(:get, "https://kentshikama.com/api/openid_connect/jwks.json")
.with(headers: {"Accept": "*/*", "Accept-Encoding": "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"Host": "kentshikama.com", "User-Agent": "Ruby"})
.to_return(status: 200,
body: "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"n\":\"qpW\",\"use\":\"sig\"}]}", headers: {})
post :create, redirect_uris: ["http://localhost"], client_name: "diaspora client",
response_types: [], grant_types: [], application_type: "web", contacts: [],
logo_uri: "http://example.com/logo.png", client_uri: "http://example.com/client",
policy_uri: "http://example.com/policy", tos_uri: "http://example.com/tos",
sector_identifier_uri: "http://example.com/uris", subject_type: "pairwise",
token_endpoint_auth_method: "private_key_jwt",
jwks_uri: "https://kentshikama.com/api/openid_connect/jwks.json"
client_json = JSON.parse(response.body)
expect(client_json["client_id"].length).to eq(32)
expect(client_json["ppid"]).to eq(true)
end
end
context "when redirect uri is missing" do
it "should return a invalid_client_metadata error" do
post :create, response_types: [], grant_types: [], application_type: "web", contacts: [],

View file

@ -328,6 +328,13 @@ FactoryGirl.define do
sector_identifier_uri "https://example.com/uri"
end
factory :o_auth_application_with_ppid_with_specific_id, class: Api::OpenidConnect::OAuthApplication do
client_name "Diaspora Test Client"
redirect_uris %w(http://localhost:3000/)
ppid true
sector_identifier_uri "https://example.com/uri"
end
factory :o_auth_application_with_multiple_redirects, class: Api::OpenidConnect::OAuthApplication do
client_name "Diaspora Test Client"
redirect_uris %w(http://localhost:3000/ http://localhost/)

1
spec/fixtures/jwks.json vendored Normal file
View file

@ -0,0 +1 @@
{"keys": [{"use": "enc", "e": "AQAB", "d": "lZQv0_81euRLeUYU84Aodh0ar7ymDlzWP5NMra4Jklkb-lTBWkI-u4RMsPqGYyW3KHRoL_pgzZXSzQx8RLQfER6timRWb--NxMMKllZubByU3RqH2ooNuocJurspYiXkznPW1Mg9DaNXL0C2hwWPQHTeUVISpjgi5TCOV1ccWVyksFruya_VNL1CIByB-L0GL1rqbKv32cDwi2A3_jJa61cpzfLSIBe-lvCO6tuiDsR4qgJnUwnndQFwEI_4mLmD3iNWXrc8N-poleV8mBfMqBB5fWwy_ZTFCpmQ5AywGmctaik_wNhMoWuA4tUfY6_1LdKld-5Cjq55eLtuJjtvuQ", "n": "tx3Hjdbc19lkTiohbJrNj4jf2_90MEE122CRrwtFu6saDywKcG7Bi7w2FMAK2oTkuWfqhWRb5BEGmnSXdiCEPO5d-ytqP3nwlZXHaCDYscpP8bB4YLhvCn7R8Efw6gwQle24QPRP3lYoFeuUbDUq7GKA5SfaZUvWoeWjqyLIaBspKQsC26_Umx1E4IXLrMSL6nkRnrYcVZBAXrYCeTP1XtsV38_lZVJfHSaJaUy4PKaj3yvgm93EV2CXybPti7CCMXZ34VqqWiF64pQjZsPu3ZTr7ha_TTQq499-zYRQNDvIVsBDLQQIgrbctuGqj6lrXb31Jj3JIEYqH_4h5X9d0Q", "q": "1q-r-bmMFbIzrLK2U3elksZq8CqUqZxlSfkGMZuVkxgYMS-e4FPzEp2iirG-eO11aa0cpMMoBdTnVdGJ_ZUR93w0lGf9XnQAJqxP7eOsrUoiW4VWlWH4WfOiLgpO-pFtyTz_JksYYaotc_Z3Zy-Szw6a39IDbuYGy1qL-15oQuc", "p": "2lrYPppRbcQWu4LtWN6tOVUrtCOPv1eLTKTc7q8vCMcem1Ox5QFB7KnUtNZ5Ni7wnZUeVDfimNebtjNsGvDSrpgIlo9dEnFBQsQIkzZ2SkoYfgmF8hNdi6P-BfRjdgYouy4c6xAnGDgSMTip1YnPRyvbMaoYT9E_tEcBW5wOeoc", "kid": "a0", "kty": "RSA"}, {"use": "sig", "e": "AQAB", "d": "DodXDEtkovWWGsMEXYy_nEEMCWyROMOebCnCv0ey3i4M4bh2dmwqgz0e-IKQAFlGiMkidGL1lNbq0uFS04FbuRAR06dYw1cbrNbDdhrWFxKTd1L5D9p-x-gW-YDWhpI8rUGRa76JXkOSxZUbg09_QyUd99CXAHh-FXi_ZkIKD8hK6FrAs68qhLf8MNkUv63DTduw7QgeFfQivdopePxyGuMk5n8veqwsUZsklQkhNlTYQqeM1xb2698ZQcNYkl0OssEsSJKRjXt-LRPowKrdvTuTo2p--HMI0pIEeFs7H_u5OW3jihjvoFClGPynHQhgWmQzlQRvWRXh6FhDVqFeGQ", "n": "zfZzttF7HmnTYwSMPdxKs5AoczbNS2mOPz-tN1g4ljqI_F1DG8cgQDcN_VDufxoFGRERo2FK6WEN41LhbGEyP6uL6wW6Cy29qE9QZcvY5mXrncndRSOkNcMizvuEJes_fMYrmP_lPiC6kWiqItTk9QBWqJfiYKhCx9cSDXsBmJXn3KWQCVHvj1ANFWW0CWLMKlWN-_NMNLIWJN_pEAocTZMzxSFBK1b5_5J8ZS7hfWRF6MQmjsJcz2jzA21SQZNpre3kwnTGRSwo05sAS-TyeadDqQPWgbqX69UzcGq5irhzN8cpZ_JaTk3Y_uV6owanTZLVvCgdjaAnMYeZhb0KFw", "q": "5E5XKK5njT-zzRqqTeY2tgP9PJBACeaH_xQRHZ_1ydE7tVd7HdgdaEHfQ1jvKIHFkknWWOBAY1mlBc4YDirLShB_voShD8C-Hx3nF5sne5fleVfU-sZy6Za4B2U75PcE62oZgCPauOTAEm9Xuvrt5aMMovyzR8ecJZhm9bw7naU", "p": "5vJHCSM3H3q4RltYzENC9RyZZV8EUmpkv9moyguT5t-BUGA-T4W_FGIxzOPXRWOckIplKkoDKhavUeNmTZMCUcue0nkICSJpvNE4Nb2p5PZk_QqSdQNvCasQtdojEG0AmfVD85SU551CYxJdLdDFOqyK2entpMr8lhokem189As", "kid": "a1", "kty": "RSA"}, {"use": "sig", "crv": "P-256", "kty": "EC", "y": "Yg4IRzHBMIsuQK2Oz0Uukp1aNDnpdoyk6QBMtmfGHQQ", "x": "L0WUeVlc9r6YJd6ie9duvOU1RHwxSkJKA37IK9B4Bpc", "kid": "a2"}, {"use": "enc", "crv": "P-256", "kty": "EC", "y": "E6E6g5_ziIZvfdAoACctnwOhuQYMvQzA259aftPn59M", "x": "Yu8_BQE2L0f1MqnK0GumZOaj_77Tx70-LoudyRUnLM4", "kid": "a3"}]}

View file

@ -7,6 +7,16 @@ describe Api::OpenidConnect::TokenEndpoint, type: :request do
o_auth_application: client, user: bob, redirect_uri: "http://localhost:3000/", scopes: ["openid"])
}
let!(:code) { auth.create_code }
let!(:client_with_specific_id) { FactoryGirl.create(:o_auth_application_with_ppid_with_specific_id) }
let!(:auth_with_specific_id) do
client_with_specific_id.client_id = "14d692cd53d9c1a9f46fd69e0e57443e"
client_with_specific_id.jwks_file = jwks_file_path
client_with_specific_id.save!
Api::OpenidConnect::Authorization.find_or_create_by(
o_auth_application: client_with_specific_id,
user: bob, redirect_uri: "http://localhost:3000/", scopes: ["openid"])
end
let!(:code_with_specific_id) { auth_with_specific_id.create_code }
describe "the authorization code grant type" do
context "when the authorization code is valid" do
@ -53,6 +63,44 @@ describe Api::OpenidConnect::TokenEndpoint, type: :request do
end
end
context "when the authorization code is valid with jwt bearer" do
before do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",
redirect_uri: "http://localhost:3000/", code: code_with_specific_id,
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQog"
end
it "should return a valid id token" do
json = JSON.parse(response.body)
encoded_id_token = json["id_token"]
decoded_token = OpenIDConnect::ResponseObject::IdToken.decode encoded_id_token,
Api::OpenidConnect::IdTokenConfig::PUBLIC_KEY
expected_guid = bob.pairwise_pseudonymous_identifiers.find_by(identifier: "https://example.com/uri").guid
expect(decoded_token.sub).to eq(expected_guid)
expect(decoded_token.exp).to be > Time.zone.now.utc.to_i
end
it "should return a valid access token" do
json = JSON.parse(response.body)
encoded_id_token = json["id_token"]
decoded_token = OpenIDConnect::ResponseObject::IdToken.decode encoded_id_token,
Api::OpenidConnect::IdTokenConfig::PUBLIC_KEY
access_token = json["access_token"]
access_token_check_num = UrlSafeBase64.encode64(OpenSSL::Digest::SHA256.digest(access_token)[0, 128 / 8])
expect(decoded_token.at_hash).to eq(access_token_check_num)
end
it "should not allow code to be reused" do
auth_with_specific_id.reload
expect(auth_with_specific_id.code).to eq(nil)
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",
client_id: client.client_id, client_secret: client.client_secret,
redirect_uri: "http://localhost:3000/", code: code_with_specific_id
expect(JSON.parse(response.body)["error"]).to eq("invalid_grant")
end
end
context "when the authorization code is not valid" do
it "should return an invalid grant error" do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",
@ -61,6 +109,45 @@ describe Api::OpenidConnect::TokenEndpoint, type: :request do
end
end
context "when the client assertion is in an invalid format" do
before do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",
redirect_uri: "http://localhost:3000/", code: code_with_specific_id,
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: "invalid_client_assertion.random"
end
it "should return an error" do
expect(response.body).to include "invalid_request"
end
end
context "when the client assertion is not matching with jwks keys" do
before do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",
redirect_uri: "http://localhost:3000/", code: code_with_specific_id,
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQoe"
end
it "should return an error" do
expect(response.body).to include "invalid_grant"
end
end
context "when kid doesn't exist in jwks keys" do
before do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",
redirect_uri: "http://localhost:3000/", code: code_with_specific_id,
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: "ewogIGFsZzogUlMyNTYsCiAga2lkOiBpbnZhbGlkX2tpZAp9Cg.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ."
end
it "should return an error" do
expect(response.body).to include "invalid_request"
end
end
context "when the client is unregistered" do
it "should return an error" do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code", code: auth.refresh_token,
@ -69,6 +156,19 @@ describe Api::OpenidConnect::TokenEndpoint, type: :request do
end
end
context "when the client is unregistered with jwks keys" do
before do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",
redirect_uri: "http://localhost:3000/", code: code_with_specific_id,
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.ewogIGF1ZDogWwogICAgaHR0cHM6Ly9rZW50c2hpa2FtYS5jb20vYXBpL29wZW5pZF9jb25uZWN0L2FjY2Vzc190b2tlbnMKICBdLAogIGlzczogMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2QsCiAganRpOiAwbWNycmVZSCwKICBleHA6IDE0NDMxNzA4OTEuMzk3NDU2LAogIGlhdDogMTQ0MzE3MDI5MS4zOTc0NTYsCiAgc3ViOiAxNGQ2OTJjZDUzZDljMWE5ZjQ2ZmQ2OWUwZTU3NDQzZAp9Cg.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQog"
end
it "should return an error" do
expect(response.body).to include "invalid_request"
end
end
context "when the code field is missing" do
it "should return an invalid request error" do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",

View file

@ -59,6 +59,10 @@ def photo_fixture_name
@photo_fixture_name = File.join(File.dirname(__FILE__), "fixtures", "button.png")
end
def jwks_file_path
@jwks_file = "../../spec/fixtures/jwks.json"
end
# Force fixture rebuild
FileUtils.rm_f(Rails.root.join("tmp", "fixture_builder.yml"))