|
1 | 1 | # frozen_string_literal: true |
2 | 2 |
|
3 | | -require 'jwt' |
| 3 | +require "jwt" |
4 | 4 |
|
5 | | -RSpec.describe 'using OAuth2 with Google' do |
| 5 | +RSpec.describe "using OAuth2 with Google" do |
6 | 6 | # This describes authenticating to a Google API via a service account. |
7 | 7 | # See their docs: https://developers.google.com/identity/protocols/OAuth2ServiceAccount |
8 | 8 |
|
9 | | - describe 'via 2-legged JWT assertion' do |
| 9 | + describe "via 2-legged JWT assertion" do |
10 | 10 | let(:client) do |
11 | 11 | OAuth2::Client.new( |
12 | | - '', |
13 | | - '', |
14 | | - site: 'https://accounts.google.com', |
15 | | - authorize_url: '/o/oauth2/auth', |
16 | | - token_url: '/o/oauth2/token', |
17 | | - auth_scheme: :request_body |
| 12 | + "", |
| 13 | + "", |
| 14 | + site: "https://accounts.google.com", |
| 15 | + authorize_url: "/o/oauth2/auth", |
| 16 | + token_url: "/o/oauth2/token", |
| 17 | + auth_scheme: :request_body, |
18 | 18 | ) |
19 | 19 | end |
20 | 20 |
|
21 | 21 | # These are taken directly from Google's documentation example: |
22 | 22 |
|
23 | 23 | let(:required_claims) do |
24 | 24 | { |
25 | | - 'iss' => '761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com', |
| 25 | + "iss" => "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com", |
26 | 26 | # The email address of the service account. |
27 | 27 |
|
28 | | - 'scope' => 'https://www.googleapis.com/auth/devstorage.readonly https://www.googleapis.com/auth/prediction', |
| 28 | + "scope" => "https://www.googleapis.com/auth/devstorage.readonly https://www.googleapis.com/auth/prediction", |
29 | 29 | # A space-delimited list of the permissions that the application requests. |
30 | 30 |
|
31 | | - 'aud' => 'https://www.googleapis.com/oauth2/v4/token', |
| 31 | + "aud" => "https://www.googleapis.com/oauth2/v4/token", |
32 | 32 | # A descriptor of the intended target of the assertion. When making an access token request this value |
33 | 33 | # is always https://www.googleapis.com/oauth2/v4/token. |
34 | 34 |
|
35 | | - 'exp' => Time.now.to_i + 3600, |
| 35 | + "exp" => Time.now.to_i + 3600, |
36 | 36 | # The expiration time of the assertion, specified as seconds since 00:00:00 UTC, January 1, 1970. This value |
37 | 37 | # has a maximum of 1 hour after the issued time. |
38 | 38 |
|
39 | | - 'iat' => Time.now.to_i, |
| 39 | + "iat" => Time.now.to_i, |
40 | 40 | # The time the assertion was issued, specified as seconds since 00:00:00 UTC, January 1, 1970. |
41 | 41 | } |
42 | 42 | end |
43 | 43 |
|
44 | 44 | let(:optional_claims) do |
45 | 45 | { |
46 | | - 'sub' => 'some.user@example.com', |
| 46 | + "sub" => "some.user@example.com", |
47 | 47 | # The email address of the user for which the application is requesting delegated access. |
48 | 48 | } |
49 | 49 | end |
50 | 50 |
|
51 | | - let(:algorithm) { 'RS256' } |
| 51 | + let(:algorithm) { "RS256" } |
52 | 52 | # Per Google: "Service accounts rely on the RSA SHA-256 algorithm" |
53 | 53 |
|
54 | 54 | let(:key) do |
55 | 55 | begin |
56 | | - OpenSSL::PKCS12.new(File.read('spec/fixtures/google_service_account_key.p12'), 'notasecret').key |
| 56 | + OpenSSL::PKCS12.new(File.read("spec/fixtures/google_service_account_key.p12"), "notasecret").key |
57 | 57 | # This simulates the .p12 file that Google gives you to download and keep somewhere. This is meant to |
58 | 58 | # illustrate extracting the key and using it to generate the JWT. |
59 | 59 | rescue OpenSSL::PKCS12::PKCS12Error |
|
73 | 73 | client.connection = Faraday.new(client.site, client.options[:connection_opts]) do |builder| |
74 | 74 | builder.request :url_encoded |
75 | 75 | builder.adapter :test do |stub| |
76 | | - stub.post('https://accounts.google.com/o/oauth2/token') do |token_request| |
| 76 | + stub.post("https://accounts.google.com/o/oauth2/token") do |token_request| |
77 | 77 | @request_body = Rack::Utils.parse_nested_query(token_request.body).transform_keys(&:to_sym) |
78 | 78 |
|
79 | 79 | [ |
80 | 80 | 200, |
81 | 81 |
|
82 | 82 | { |
83 | | - 'Content-Type' => 'application/json', |
| 83 | + "Content-Type" => "application/json", |
84 | 84 | }, |
85 | 85 |
|
86 | 86 | { |
87 | | - 'access_token' => '1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M', |
88 | | - 'token_type' => 'Bearer', |
89 | | - 'expires_in' => 3600, |
| 87 | + "access_token" => "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M", |
| 88 | + "token_type" => "Bearer", |
| 89 | + "expires_in" => 3600, |
90 | 90 | }.to_json, |
91 | 91 | ] |
92 | 92 | end |
93 | 93 | end |
94 | 94 | end |
95 | 95 | end |
96 | 96 |
|
97 | | - context 'when passing the required claims' do |
| 97 | + context "when passing the required claims" do |
98 | 98 | let(:claims) { required_claims } |
99 | 99 |
|
100 | | - it 'sends a JWT with the 5 keys' do |
| 100 | + it "sends a JWT with the 5 keys" do |
101 | 101 | client.assertion.get_token(claims, encoding_options) |
102 | 102 |
|
103 | | - expect(@request_body).not_to be_nil, 'No access token request was made!' |
104 | | - expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer') |
| 103 | + expect(@request_body).not_to be_nil, "No access token request was made!" |
| 104 | + expect(@request_body[:grant_type]).to eq("urn:ietf:params:oauth:grant-type:jwt-bearer") |
105 | 105 | expect(@request_body[:assertion]).to be_a(String) |
106 | 106 |
|
107 | 107 | payload, header = JWT.decode(@request_body[:assertion], key, true, algorithm: algorithm) |
108 | | - expect(header['alg']).to eq('RS256') |
| 108 | + expect(header["alg"]).to eq("RS256") |
109 | 109 | expect(payload.keys).to match_array(%w[iss scope aud exp iat]) |
110 | 110 |
|
111 | 111 | # Note that these specifically do _not_ include the 'sub' claim, which is indicated as being 'required' |
|
118 | 118 | end |
119 | 119 | end |
120 | 120 |
|
121 | | - context 'when including the optional `sub` claim' do |
| 121 | + context "when including the optional `sub` claim" do |
122 | 122 | let(:claims) { required_claims.merge(optional_claims) } |
123 | 123 |
|
124 | | - it 'sends a JWT with the 6 keys' do |
| 124 | + it "sends a JWT with the 6 keys" do |
125 | 125 | client.assertion.get_token(claims, encoding_options) |
126 | 126 |
|
127 | | - expect(@request_body).not_to be_nil, 'No access token request was made!' |
128 | | - expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer') |
| 127 | + expect(@request_body).not_to be_nil, "No access token request was made!" |
| 128 | + expect(@request_body[:grant_type]).to eq("urn:ietf:params:oauth:grant-type:jwt-bearer") |
129 | 129 | expect(@request_body[:assertion]).to be_a(String) |
130 | 130 |
|
131 | 131 | payload, header = JWT.decode(@request_body[:assertion], key, true, algorithm: algorithm) |
132 | | - expect(header['alg']).to eq('RS256') |
| 132 | + expect(header["alg"]).to eq("RS256") |
133 | 133 | expect(payload.keys).to match_array(%w[iss scope aud exp iat sub]) |
134 | 134 |
|
135 | 135 | payload.each do |key, value| |
|
0 commit comments