Skip to content
This repository has been archived by the owner on Jul 19, 2024. It is now read-only.

[WIP] Support Microsoft apps on v2 endpoint #39

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@ AllCops:
Exclude:
- 'spec/fixtures/**/*'

Metrics/LineLength:
Enabled: false

Metrics/BlockLength:
Enabled: false

Style/Encoding:
Enabled: false
214 changes: 105 additions & 109 deletions lib/omniauth/strategies/azure_activedirectory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,81 +21,96 @@
#-------------------------------------------------------------------------------

require 'jwt'
require 'omniauth'
require 'omniauth/strategies/oauth2'
require 'openssl'
require 'securerandom'

module OmniAuth
module Strategies
# A strategy for authentication against Azure Active Directory.
class AzureActiveDirectory
# rubocop:disable Metrics/ClassLength
class AzureActiveDirectory < OmniAuth::Strategies::OAuth2
include OmniAuth::AzureActiveDirectory
include OmniAuth::Strategy

class OAuthError < StandardError; end
BASE_SCOPES = %w[openid profile email].freeze
DEFAULT_RESPONSE_MODE = 'form_post'.freeze
DEFAULT_RESPONSE_TYPE = 'code id_token'.freeze
DEFAULT_TENANT = 'common'.freeze

option :name, 'azure_activedirectory'

##
# The client id (key) and tenant must be configured when the OmniAuth
# middleware is installed. Example:
#
# require 'omniauth'
# require 'omniauth-azure-activedirectory'
#
# use OmniAuth::Builder do
# provider :azure_activedirectory, ENV['AAD_KEY'], ENV['AAD_TENANT']
# end
#
args [:client_id, :tenant]
option :client_id, nil
option :tenant, nil
option :client_secret, nil
option :response_mode, DEFAULT_RESPONSE_MODE
option :response_type, DEFAULT_RESPONSE_TYPE
option :scope, BASE_SCOPES.join(' ')
option :tenant, DEFAULT_TENANT
option :verify_iss, false

credentials { { code: @code } }
uid { claims['sub'] }

# Field renaming is an attempt to fit the OmniAuth recommended schema as
# best as possible.
#
# @see https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema
uid { @claims['sub'] }
info do
{ name: @claims['name'],
email: @claims['email'] || @claims['upn'],
first_name: @claims['given_name'],
last_name: @claims['family_name'] }
{
# Since name is not always present, default to oid because `name` is
# required for an auth_hash to be valid. The name should not be used
# for anything except display.
name: claims['name'] || claims['oid'],
# Email is only included in personal accounts requests. For sign-ins
# with an AD account, the preferred_username key might be an email,
# but not always; it could be a phone number or just a generic
# username.
email: claims['email'],
preferred_username: claims['preferred_username'],
oid: claims['oid'],
tid: claims['tid']
}
end
credentials { { code: @code } }
extra do
{ session_state: @session_state,
raw_info:
{ id_token: @id_token,
id_token_claims: @claims,
id_token_header: @header } }
id_token_claims: claims,
id_token_header: header } }
end

DEFAULT_RESPONSE_TYPE = 'code id_token'
DEFAULT_RESPONSE_MODE = 'form_post'
uid { claims['sub'] }

##
# Overridden method from OmniAuth::Strategy. This is the first step in the
# authentication process.
def request_phase
redirect authorize_endpoint_url
def client
options.client_options.authorize_url = URI(openid_config['authorization_endpoint'])
options.client_options.token_url = URI(openid_config['token_endpoint'])

super
end

##
# Overridden method from OmniAuth::Strategy. This is the second step in
# the authentication process. It is called after the user enters
# credentials at the authorization endpoint.
def callback_phase
error = request.params['error_reason'] || request.params['error']
fail(OAuthError, error) if error
@session_state = request.params['session_state']
@id_token = request.params['id_token']
@code = request.params['code']
@claims, @header = validate_and_parse_id_token(@id_token)
validate_chash(@code, @claims, @header)
def callback_url
full_host + script_name + callback_path
end

def authorize_params
options.authorize_params[:nonce] = new_nonce
options.authorize_params[:response_mode] = options.response_mode
options.authorize_params[:response_type] = options.response_type

super
end

private

def claims
@claims ||= decoded_token[0]
end

def header
@header ||= decoded_token[1]
end

def decoded_token
@session_state = request.params['session_state']
@id_token = access_token.params['id_token']
@decoded_token ||= validate_and_parse_id_token(@id_token)
end

##
# Constructs a one-time-use authorize_endpoint. This method will use
# a new nonce on each invocation.
Expand All @@ -105,8 +120,6 @@ def authorize_endpoint_url
uri = URI(openid_config['authorization_endpoint'])
uri.query = URI.encode_www_form(client_id: client_id,
redirect_uri: callback_url,
response_mode: response_mode,
response_type: response_type,
nonce: new_nonce)
uri.to_s
end
Expand All @@ -118,15 +131,8 @@ def authorize_endpoint_url
# @return String
def client_id
return options.client_id if options.client_id
fail StandardError, 'No client_id specified in AzureAD configuration.'
end

##
# The expected id token issuer taken from the discovery endpoint.
#
# @return String
def issuer
openid_config['issuer']
raise StandardError, 'No client_id specified in AzureAD configuration.'
end

##
Expand Down Expand Up @@ -166,12 +172,23 @@ def fetch_signing_keys
#
# @return Hash
def fetch_openid_config
JSON.parse(Net::HTTP.get(URI(openid_config_url)))
JSON.parse(
Net::HTTP.get(
URI(openid_config_url)
)
)
rescue JSON::ParserError
raise StandardError, 'Unable to fetch OpenId configuration for ' \
'AzureAD tenant.'
end

# The expected id token issuer taken from the discovery endpoint.
#
# @return String
def issuer
openid_config['issuer']
end

##
# Generates a new nonce for one time use. Stores it in the session so
# multiple users don't share nonces. All nonces should be generated by
Expand All @@ -195,7 +212,7 @@ def openid_config
#
# @return String
def openid_config_url
"https://login.windows.net/#{tenant}/.well-known/openid-configuration"
"https://login.microsoftonline.com/#{options.tenant}/v2.0/.well-known/openid-configuration/"
end

##
Expand All @@ -207,26 +224,6 @@ def read_nonce
session.delete('omniauth-azure-activedirectory.nonce')
end

##
# The response_type that will be set in the authorization request query
# parameters. Can be overridden by the client, but it shouldn't need to
# be.
#
# @return String
def response_type
options[:response_type] || DEFAULT_RESPONSE_TYPE
end

##
# The response_mode that will be set in the authorization request query
# parameters. Can be overridden by the client, but it shouldn't need to
# be.
#
# @return String
def response_mode
options[:response_mode] || DEFAULT_RESPONSE_MODE
end

##
# The keys used to sign the id token JWTs. This is just a memoized version
# of #fetch_signing_keys.
Expand All @@ -243,17 +240,8 @@ def signing_keys
# @return String
def signing_keys_url
return openid_config['jwks_uri'] if openid_config.include? 'jwks_uri'
fail StandardError, 'No jwks_uri in OpenId config response.'
end

##
# The tenant of the calling application. Note that this must be
# explicitly configured when installing the AzureAD OmniAuth strategy.
#
# @return String
def tenant
return options.tenant if options.tenant
fail StandardError, 'No tenant specified in AzureAD configuration.'
raise StandardError, 'No jwks_uri in OpenId config response.'
end

##
Expand All @@ -263,31 +251,34 @@ def tenant
# See OpenId Connect Core 3.1.3.7 and 3.2.2.11.
#
# @return Claims, Header
#
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
def validate_and_parse_id_token(id_token)
# The second parameter is the public key to verify the signature.
# However, that key is overridden by the value of the executed block
# if one is present.
#
# If you're thinking that this looks ugly with the raw nil and boolean,
# see https://github.com/jwt/ruby-jwt/issues/59.
jwt_claims, jwt_header =
claims, token_header =
JWT.decode(id_token, nil, true, verify_options) do |header|
# There should always be one key from the discovery endpoint that
# matches the id in the JWT header.
x5c = (signing_keys.find do |key|
key['kid'] == header['kid']
end || {})['x5c']
if x5c.nil? || x5c.empty?
fail JWT::VerificationError,
'No keys from key endpoint match the id token'
raise JWT::VerificationError, 'No keys from key endpoint match the id token'
end

# The key also contains other fields, such as n and e, that are
# redundant. x5c is sufficient to verify the id token.
OpenSSL::X509::Certificate.new(JWT.base64url_decode(x5c.first)).public_key
end
return jwt_claims, jwt_header if jwt_claims['nonce'] == read_nonce
fail JWT::DecodeError, 'Returned nonce did not match.'

@code = request.params['code']
validate_chash(@code, claims, token_header)
return claims, token_header if claims['nonce'] == read_nonce

raise JWT::DecodeError, 'Returned nonce did not match.'
end
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize

##
# Verifies that the c_hash the id token claims matches the authorization
Expand All @@ -297,13 +288,15 @@ def validate_and_parse_id_token(id_token)
# @param Hash claims
# @param Hash header
def validate_chash(code, claims, header)
return if claims['c_hash'].nil?

# This maps RS256 -> sha256, ES384 -> sha384, etc.
algorithm = (header['alg'] || 'RS256').sub(/RS|ES|HS/, 'sha')
full_hash = OpenSSL::Digest.new(algorithm).digest code
c_hash = JWT.base64url_encode full_hash[0..full_hash.length / 2 - 1]
return if c_hash == claims['c_hash']
fail JWT::VerificationError,
'c_hash in id token does not match auth code.'

raise JWT::VerificationError, 'c_hash in id token does not match auth code.'
end

##
Expand All @@ -314,15 +307,18 @@ def validate_chash(code, claims, header)
#
# @return Hash
def verify_options
{ verify_expiration: true,
verify_not_before: true,
verify_iat: true,
verify_iss: true,
'iss' => issuer,
{
aud: client.id,
iss: issuer,
verify_aud: true,
'aud' => client_id }
verify_expiration: true,
verify_iat: true,
verify_iss: options.verify_iss,
verify_not_before: true
}
end
end
# rubocop:enable Metrics/ClassLength
end
end

Expand Down
3 changes: 2 additions & 1 deletion omniauth-azure-activedirectory.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ Gem::Specification.new do |s|

s.add_runtime_dependency 'jwt', '~> 1.5'
s.add_runtime_dependency 'omniauth', '~> 1.1'
s.add_runtime_dependency 'omniauth-oauth2', '~> 1.4'

s.add_development_dependency 'rake', '~> 10.4'
s.add_development_dependency 'rspec', '~> 3.3'
s.add_development_dependency 'rubocop', '~> 0.32'
s.add_development_dependency 'simplecov', '~> 0.10'
s.add_development_dependency 'webmock', '~> 1.21'
s.add_development_dependency 'webmock', '~> 2.3'
end
2 changes: 1 addition & 1 deletion spec/fixtures/id_token.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyJ9.eyJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9idW5jaC1vZi1yYW5kb20tY2hhcnMiLCJuYW1lIjoiSm9obiBTbWl0aCIsImF1ZCI6InRoZSBjbGllbnQgaWQiLCJub25jZSI6Im15IG5vbmNlIiwiZ2l2ZW5fbmFtZSI6IkpvaG4iLCJmYW1pbHlfbmFtZSI6InNtaXRoIiwiZW1haWwiOiJqc21pdGhAY29udG9zby5jb20iLCJjX2hhc2giOiJWcFRRaWk1VF84cmd3eEEtV3RiMkJ3In0.Xz9SL1dm9xeJ3YtBIwSpL7SHEMr5lsL32mkJIugoAt7rNhj2ZauN77_N3skU9FIRTVb_XBFHrLo1AXion7RWoOGAMk8xnuQRlhamGoWsjttWE9oO6J6kuQPSDBvLTA88UqLoGNezDwFNfgUFQq-66m33ZWdkiNOFoFZ_In6DtAwxHZZUys-KoYD3iCbviUoBzU57aV-SBsWyComq39pDGpw03qZoa_xgRfujdVHG1DKlO5VG79kUE3ySYWJyBYVKUdAzjH1iotjpPA1svtqytn4CUldAMi4nnf7iq5SCJMb4ucu0mN6AhJH9ktY--fGY6_OidyiDe4F57ZzLw-3jOQ
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYzEyMyJ9.eyJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9idW5jaC1vZi1yYW5kb20tY2hhcnMiLCJuYW1lIjoiSm9obiBTbWl0aCIsImF1ZCI6InRoZSBjbGllbnQgaWQiLCJub25jZSI6Im15IG5vbmNlIiwiZW1haWwiOiJqc21pdGhAY29udG9zby5jb20iLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqc21pdGhAY29udG9zby5jb20iLCJ2ZXIiOiIyLjAifQ.eFqQS4R9OVqorc4qSY9bOvsxmmL3OQJ96IHD9JD6Y6jiD_YTAifNP-FahOqRZjqh7bCdU1XUnvKM7RC0z_CRFkHkOJ9bzV0iTsBYSpeX9v4QUSAhtxKvcrAufCyT-NOViv4WvvnYbZDQRO7BxU80OI9poSgkw8rKST28EA-cB2c
2 changes: 1 addition & 1 deletion spec/fixtures/id_token_bad_audience.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyJ9.eyJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9idW5jaC1vZi1yYW5kb20tY2hhcnMiLCJuYW1lIjoiSm9obiBTbWl0aCIsImF1ZCI6Im5vdCB0aGUgY2xpZW50IGlkIiwibm9uY2UiOiJteSBub25jZSIsImdpdmVuX25hbWUiOiJKb2huIiwiZmFtaWx5X25hbWUiOiJzbWl0aCIsImVtYWlsIjoianNtaXRoQGNvbnRvc28uY29tIn0.gGCFTcGDBb2P5tPRj2ojUUiwOJoSslQlxEVTElZY6FCzCZzcypsnrYOB9Adkztp2AWF4wW9fT58lqXwaahKquXtK4wyK4KoNBxXBhS4sDMeNpVkK4914tT_6gecvyUsI_tlJaS0epd5c0mN9-1QjgvNEirY1L-XYaT290LmLYVYIFTEJXoSlnwvv081k0txdJZKr14JXd_bSLUbhGSd-NcGZkJuVmg2F_C65zd1wUsQOkV2iVdJ-ycaDJklv8-DFfDFIHQio8S9yqyieHwArRmTW9HzcoHhWBh2MIItszoTSbmQEF062NtNBPW8gyk1OSot5X9klJUhgPAAFqJ0TJQ
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYzEyMyJ9.eyJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9idW5jaC1vZi1yYW5kb20tY2hhcnMiLCJuYW1lIjoiSm9obiBTbWl0aCIsImF1ZCI6Im5vdCB0aGUgY2xpZW50IGlkIiwibm9uY2UiOiJteSBub25jZSIsImVtYWlsIjoianNtaXRoQGNvbnRvc28uY29tIiwicHJlZmVycmVkX3VzZXJuYW1lIjoianNtaXRoQGNvbnRvc28uY29tIiwidmVyIjoiMi4wIn0.e9s97ndTZw8umsfsaLntzzjIEyiIRGRTa9mvBiYEN-rddxlkhunN2VzHmlG_7W5D8yHkwMFKrvNnF2ciw5UcMfwzL3bBDaZOm_eyl-XGVOG93407sjEU7kjUerfmiSstGFIUqw-xtBa5uAV7rpbhSBn71sS234xvUsRb7x6Vs6s
2 changes: 1 addition & 1 deletion spec/fixtures/id_token_bad_chash.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyJ9.eyJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9idW5jaC1vZi1yYW5kb20tY2hhcnMiLCJuYW1lIjoiSm9obiBTbWl0aCIsImF1ZCI6InRoZSBjbGllbnQgaWQiLCJub25jZSI6Im15IG5vbmNlIiwiZ2l2ZW5fbmFtZSI6IkpvaG4iLCJmYW1pbHlfbmFtZSI6InNtaXRoIiwiZW1haWwiOiJqc21pdGhAY29udG9zby5jb20iLCJjX2hhc2giOiI1WEhDdk9qcUgtbnJBXzNXNUJYaWJnIn0.EHPFrxMbr2EH1IZ15F3oP1yvrukadY4ggZSOxm625qPoMxfhv-QzFm0YMhkAG0cLM_LSHi_RgedDwTGuzOtIWqmheYzydnhtbIBeKmx0RSgdob6WeiTZwJ93VRiV4q82Qda41JaaIl_wdWd4lVyVstd9o9jPYMtKEVLgaNDrtHt6pPxEGCVraiaM6tVyKc5XIHu3wNaqle5UZZREU6oirwTCUhXDZkz1g5qY2-aWUdfFVsElSarJuMcxGDPD20hvx7T3D7SCHAF8WhKw3AwQyrodKWCo3cqDOz6vHymb_-ELkJc14GMbtJiyrf6XYySZZoseoI_WBDmLfKcK637xCw
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYzEyMyJ9.eyJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9idW5jaC1vZi1yYW5kb20tY2hhcnMiLCJuYW1lIjoiSm9obiBTbWl0aCIsImF1ZCI6InRoZSBjbGllbnQgaWQiLCJub25jZSI6Im15IG5vbmNlIiwiZW1haWwiOiJqc21pdGhAY29udG9zby5jb20iLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqc21pdGhAY29udG9zby5jb20iLCJ2ZXIiOiIyLjAiLCJjX2hhc2giOiJWcFRRaWk1VF84cmd3eEEtV3RiMkIyIn0.L5S6cTvXrzFCBX_wXPUfLJqnSX9GZ3LXyvW8U63sGo_RjPNNBsbAaUZ8FlUUDF_U4HMTh2Q1BHK8UxXDC7K1JtpvoytpXSkJ7TPJqpdFLQ6xtrkvKiR1StIwEK-NcbHdS4Rta3Lqwrc3_vUGa_4DNUARHeqvjzw-nG0AVaTIlLs
2 changes: 1 addition & 1 deletion spec/fixtures/id_token_bad_issuer.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyJ9.eyJpc3MiOiJodHRwczovL3N0cy5pbXBvc3Rlci5uZXQvYnVuY2gtb2YtcmFuZG9tLWNoYXJzIiwibmFtZSI6IkpvaG4gU21pdGgiLCJhdWQiOiJ0aGUgY2xpZW50IGlkIiwibm9uY2UiOiJteSBub25jZSIsImdpdmVuX25hbWUiOiJKb2huIiwiZmFtaWx5X25hbWUiOiJzbWl0aCIsImVtYWlsIjoianNtaXRoQGNvbnRvc28uY29tIn0.fwnJuRsif_Td3MfXoyADHidYJyPFdWSBBoLbVAu4Lz3-pmSln9Vgxl5KowqEKq2LX5n0aqyWVGLcoT-_G_PNXuizuNmssv5vreKLvDMpsFXt2irdwGYDCRki7KQPBk3bn12YjBzE2EqiRy7dTEG_0vWoh1RqoNP72BBL8xYQUlIOFleZhT5KGNYbh7rvcmDq7aA-xdaXT3QJfHCHpitW_zVzZ5Gok_awcdx_v3r3eFbG2IT0PfmT40Ljia0aP2i60KgsOLLHarYO51KFNDEfr1pUDf4IweaEzstbVLwk-_5ulJ5QgByhNJrmWDfrGeCQRk0SO2XX-EUsVn5ySVJ5CQ
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYzEyMyJ9.eyJpc3MiOiJodHRwczovL3N0cy5pbXBvc3Rlci5uZXQvYnVuY2gtb2YtcmFuZG9tLWNoYXJzIiwibmFtZSI6IkpvaG4gU21pdGgiLCJhdWQiOiJ0aGUgY2xpZW50IGlkIiwibm9uY2UiOiJteSBub25jZSIsImVtYWlsIjoianNtaXRoQGNvbnRvc28uY29tIiwicHJlZmVycmVkX3VzZXJuYW1lIjoianNtaXRoQGNvbnRvc28uY29tIiwidmVyIjoiMi4wIn0.jkUJ33TIcZ4kyXEB86cn-smKc8J_ROB6QK-u1QgzTTFZDPha-VJKLOZlZChsTXAq8LSlGIEGJAnqFxIS-Rm9nbBQK70v5YD7P0lFb1YvLwIbW1d764_bG_oAj7jPR7i9boyD7Udvivr19mIHFF6iSAQJV26o3UFa6pXsiZNvlUQ
2 changes: 1 addition & 1 deletion spec/fixtures/id_token_bad_kid.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMzQifQ.eyJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9idW5jaC1vZi1yYW5kb20tY2hhcnMiLCJuYW1lIjoiSm9obiBTbWl0aCIsImF1ZCI6InRoZSBjbGllbnQgaWQiLCJub25jZSI6Im15IG5vbmNlIiwiZ2l2ZW5fbmFtZSI6IkpvaG4iLCJmYW1pbHlfbmFtZSI6InNtaXRoIiwiZW1haWwiOiJqc21pdGhAY29udG9zby5jb20iLCJjX2hhc2giOiI1WEhDdk9qcUgtbnJBXzNXNUJYaWJnIn0.gWGBc9rH30SN17Ikm1CjqIYAyzFHX0yeRQu85sVYLE3r5k26bjS3R6rTJcCQlYqHPRdoPcnkUgT1QVbdThw34ICrODoavs7I5QYEn_jKP9zM4UJEKQaCLBAtitzrk1KDEf0GLcNKif-MYu7MiQfoOzwCGWfIs-vgqk4lv0JUs9OlSLp5LHru7G3jKy6Qswbrpxpjzm9I8BnKEdhUfhz4P6wIf9KLMmhgeGQdQ6wBuxPmOf9r6EKIij2AENhFp1qP90m8kXq9tIt5FZFjwIs_G6spLl0gQXyx0qC8rP5JTkqwrBUieWU-BRqVdax8YxA0iDKzZyfsMV92yVcZT6S_NQ
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Inp4Yzg5MCJ9.eyJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9idW5jaC1vZi1yYW5kb20tY2hhcnMiLCJuYW1lIjoiSm9obiBTbWl0aCIsImF1ZCI6InRoZSBjbGllbnQgaWQiLCJub25jZSI6Im15IG5vbmNlIiwiZW1haWwiOiJqc21pdGhAY29udG9zby5jb20iLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqc21pdGhAY29udG9zby5jb20iLCJ2ZXIiOiIyLjAifQ.I2LrcnOc4Ka3LcfJQBb4mCt4rcJF1eCIyHiQ_dMVvR5eeQTPpotxhR6gxURV-bC9gE-TZYpgal4Z28kyS_kv_Rv-mh6o6pRnRdOf9ASmruf76vaWjJkm_gmQ7tjytsWCLTsUTN0sFSk5MSwCstt6l8sEjpmvivnRi2BF85q56Vs
2 changes: 1 addition & 1 deletion spec/fixtures/id_token_bad_nonce.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyJ9.eyJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9idW5jaC1vZi1yYW5kb20tY2hhcnMiLCJuYW1lIjoiSm9obiBTbWl0aCIsImF1ZCI6InRoZSBjbGllbnQgaWQiLCJub25jZSI6Im5vdCBteSBub25jZSIsImdpdmVuX25hbWUiOiJKb2huIiwiZmFtaWx5X25hbWUiOiJzbWl0aCIsImVtYWlsIjoianNtaXRoQGNvbnRvc28uY29tIn0.KBSOCiy0sUF33akept9nPNFgmMWxiPWDBVA0dZqQaF6gQkhQu82irAQzB2Ygkh9KDeIEf0MSZWLDq0X3W3Fke4wzjrbzL-2QM4l-KgPFbJixqtJYHSPOidHSCQ8vA0v8kES3H_zqU6QisygwwXLh2ozqKXMnsrBPIAtiZz_a0vPbHtrYbb-WIrtTruMemcTt5OkbfDIttzi5EarakQg93vraIb0jK0szuAqLkXFOzcIIGPgyAvVpHZveqm99GR04tbKyTRIyJP1vZwtIpu89PKFM3soWcWd_kjAWcS81ZTzEn5_P-1QL7YTInK53acNXZHh08Fba3Um4J_KlV2KRjw
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYzEyMyJ9.eyJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9idW5jaC1vZi1yYW5kb20tY2hhcnMiLCJuYW1lIjoiSm9obiBTbWl0aCIsImF1ZCI6InRoZSBjbGllbnQgaWQiLCJub25jZSI6Im5vdCBteSBub25jZSIsImVtYWlsIjoianNtaXRoQGNvbnRvc28uY29tIiwicHJlZmVycmVkX3VzZXJuYW1lIjoianNtaXRoQGNvbnRvc28uY29tIiwidmVyIjoiMi4wIn0.YMNhaW8g-jP5dJ0_zpB0qNMi2NvysztpcB6j9C31EbrAckfhckJE4urART7MMFy_cdNEIFLTZT5yT4bj10EnBujkNmCVVmCG7KSE7hdqWkSbM_3IPNQayVOXqz6hkde_-Di2L40nZ-HvNNt6q1sRaxTj6oF6DEnZBQ2iYga3fIQ
Loading