Instantly share code, notes, and snippets.

Embed
What would you like to do?
Rails, Auth0, JWT, RS256
module Authenticable
extend ActiveSupport::Concern
# Filter to use with before_action
def authenticate_user
jwt_token
rescue JWT::DecodeError => e
render json: { error: e.message }, status: :unauthorized
end
# @return [User|nil]
def current_user
@current_user ||= User.new jwt_token.try(:first)
rescue JWT::DecodeError
nil
end
private
def jwt_token
bearer = request.env.fetch('HTTP_AUTHORIZATION', '').slice(7..-1)
rsa_public_key = OpenSSL::PKey::RSA.new(
OpenSSL::X509::Certificate.new(
Rails.configuration.auth0['signing_certificate']
).public_key
)
JWT.decode bearer, rsa_public_key, true, algorithm: 'RS256'
end
end
module App
class Application < Rails::Application
# (...)
config.auth0 = config_for(:auth0)
end
end
production: &default
# taken from auth0.com: Clients > AppJobs > Settings > Advanced Settings > Certificates
signing_certificate: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
development:
<<: *default
test:
<<: *default
# generated with `openssl req -x509 -newkey rsa:2048 -keyout privatekey.pem -out certificate.pem`
signing_certificate: |
-----BEGIN CERTIFICATE-----
MIIDtTCCAp2gAwIBAgIJAPunMNM1vt7wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTcwMjA5MDk1MTEzWhcNMTcwMzExMDk1MTEzWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAnEUQtIH4cmK9j41CC5tyk0v8CJNC803ie4vJPaYIc8Q4UXhnndi5mjb/
ao4ZjmQoJPTSpPKlBQ9u7zuq2O52usXn977Pe6c2p1gXNC6a1pDB0BWxZ3IdfqyF
rwyunPCJ4XtTSz59noD42/BaZXKq808ToR04IMpSfgTgl8uYExXTs4cFpCdN3YB9
e3/KuDN77Wl4ReWNhWb2v9Y/J0j8D4yYOTatYEPHnGm2EvvoeNoNhfb+kM4Bh5f1
fudZUjclX8fJ0dVkesGtpbe3qK/+QtncJHAXC3P1GRFr+fGK9Iytrl6Mv6SVFZ4R
yArqgU0YZwvP+dNac9KAOgOEelmZZQIDAQABo4GnMIGkMB0GA1UdDgQWBBTMTFdM
XXO4RgdUSeIVG8yy2trHZzB1BgNVHSMEbjBsgBTMTFdMXXO4RgdUSeIVG8yy2trH
Z6FJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV
BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAPunMNM1vt7wMAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGQNo4QmtPHf+ZdspVFsTqi+7slZb0it
b2zyB8xYv6AyX9bAeGBYIqw9ObuRWL8IW3mGb/MDgKyoeydHPBf8GHJRf5GVXMJz
7cnY1HJz+v+R+Em2bMqlsuRr0NTD7/vGUptuzYVtpJQXfwtLDHTZRvYXuLwr8x2o
LFtRPjsmI7LvUhvAK5bm7zhPULrVcZZdITRKHw1eePebPxDm2Z3zzUHLgfwnR8CH
nJMBQ1R0Uk7SbXo8hjJY7fpMB7UMk8O1urE5i5poBhjdw8e62EfpidSQTrMsmt+F
LCG0/53nHkYRfJNWh5YeMptEkbJfbM3JKU2MiWrZRmdJVR+0B06TnDY=
-----END CERTIFICATE-----
private_key: |
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,F4FD7F790446D8BA
zIHjysaSGXoaZucd33SIc5jt8JG0iB6d8cokxFnsy8JlsIfRaWqz+CDlN2TpTHjH
AOtV+c9Dhv8I8teCmwxzGy2XOHWY909UrhwIT3xM10OiGMwEnVJ90eBKdcGEZZUX
TnLNAM+Ip44+beRpGOya56PzoNSIE5W7CCexlZ4L/vvPhHb7jb33QoUq3EIFW1+6
xpLW8Imf61X1QCZizbBU9GMg71FqePW+X1OfwQIMX7V1lCDeTQuA5BwCT+wapFVV
m95lPvaiDkz44V7kGvREPzpgQvFdvRe6oShEEfvlmSylQtYv5568FymWlBKtkmTh
69pJZZ4jdGTU+CI5IOP2tEiLRRQYh81bd8KY027RO9buZWSB8GeNOT+6V/B/Uy7G
x5vt1/CGhZHnEFLFHNHMObF7PMyDZhuYGYz3aH5G/Asb9Z+W+XgeIWOIofP3Vfq7
0z3BfoCmwZcU+GfFfLDxkHep+QvEjc4Ax8j7vMq0E2vzzqIijVDOJHC0X7ZJF8qQ
lH137SBDnVLSspJSlqKOD5iuYQBPdchprE/+3CRZwjRXsPcwJ9IjiLtUSBvEkgd2
EmW18LgoTj7Rt8aWtx4PtgL/0eNKoKnxuHGeXWNyczh9/8BbeQT0s5I098QHJ2+w
VyWXl7qyfQDSvt6ARi+D1R/mL/AGkIEWetZmokYewfubcumw9dG1f78IDrLzQ3Ew
bk8dKjeeFqb0A5ZWcSmcgWYo03n2GtmKKT/ag/CCkUUmK4GSxIldQ4XnntalrKtJ
4kEBrHRPSJQpXz/hFg90E7Ct1EwhEIqdvBB9005DyQZMGIY8uAwCOsKy3tiyrGaS
4t6vzxGB7BsZVsi2EwK/gGTdLgiHD6CJyOLrHdeihBqnDO/zmNVamlaKpqUnjrIT
YQQDlc4Dhm/+jutyFsrXjQNiOKGVxAtXlkYmLj0fXwH3zsdpvTTOWktIdAn0Nhg7
N3rAl3TDsF7lBhDf6Aa9n9jvdp9lzhRRy0buvCBjvO99c6PWFgcOXJtdmoIwyoGq
i3/8rQTTb+gp/yjnTguXoSqDpGLCtb6fM6B0ER0F/uypyXWBi9U0pcVrJ+7wETye
8+a0Yj7Lc18S/IilWLI0a0pJEeWXJDlsICkFfYMRmm2WG/4NfHFH9GiEVN71AVQt
+r1j68kDiZdgSFI/tlw9G+fN3jcJVQ4grg4w6RnP5xSWdp7fJFH1pft6WfZy4n6f
xiFId8Cl81lJ4ptMh+kib0usR0i2zwDK8fSYIr3RmOOw3G4monSDrKGNSpVTImDD
pAI0B+AxzGU+p1v0PasQ72wLv5fx21yOZTlS/c2dC6qlfPDqUXT1PY8O4Yz1hhUS
bGujavaIenPmeFVtE/io3M5Ge+Nzv6Rt9zvuXJfrxpwnK5X6gI2mTPv0rCXrGODG
YUfGSiexYkFMSu+gZNbwg7SL715wM5Np7evJcpvALqwvLRuHlWE/7gR6vC0ywg0N
Sg8NRheEdP2LIejRXqQ5l73w3A4wSzEgipdrH4azQBWUh6F7eCLjfOjC80lRosT+
IidiK9THkzZykLlKrfuT2VaSEjmVg6vpamTG/fu6fea+35MnY6Aq1g==
-----END RSA PRIVATE KEY-----
private_key_passphrase: appjobs
module Helpers
def current_user_headers(payload_overwrites = {})
payload = {
email: 'user@example.com',
email_verified: true,
nickname: 'Current User',
iss: 'https://YOURAPP.auth0.com/',
sub: 'auth0|123456789',
aud: 'YOUR_AUD',
exp: (Time.now + 1.day).to_i,
iat: Time.now.to_i
}.merge(payload_overwrites)
@private_key ||= OpenSSL::PKey::RSA.new(
Rails.configuration.auth0['private_key'],
Rails.configuration.auth0['private_key_passphrase']
)
id_token = JWT.encode payload, @private_key, 'RS256'
{ 'Authorization' => "Bearer #{id_token}" }
end
end
require 'rails_helper'
describe 'User' do
specify 'current user info', do
get '/user', headers: current_user_headers
expect(response).to be_success
expect(response.body).to have_json_size(8)
expect(response.body).to be_json_eql('Current User'.to_json).at_path('nickname')
expect(response.body).to be_json_eql('user@example.com'.to_json).at_path('email')
end
specify 'jwt: signature has expired' do
get '/user', headers: current_user_headers(exp: Time.now - 1.day)
expect(response).not_to be_success
expect(response.body).to have_json_size(1)
expect(response.body).to be_json_eql('Signature has expired'.to_json).at_path('error')
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment