Skip to content

Commit 8b5c015

Browse files
authored
Merge pull request #253 from yskkin/id-token-verify
Add ID token verification endpoint
2 parents 8760add + 174c98f commit 8b5c015

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

lib/line/bot/client.rb

+47
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,40 @@ def revoke_channel_access_token_jwt(access_token)
145145
post(oauth_endpoint, endpoint_path, payload, headers)
146146
end
147147

148+
# Verify ID token
149+
#
150+
# @param id_token [String] ID token
151+
# @param nonce [String] Expected nonce value. Use the nonce value provided in the authorization request. Omit if the nonce value was not specified in the authorization request.
152+
# @param user_id [String] Expected user ID.
153+
#
154+
# @return [Net::HTTPResponse]
155+
def verify_id_token(id_token, nonce: nil, user_id: nil)
156+
channel_id_required
157+
158+
endpoint_path = '/oauth2/v2.1/verify'
159+
payload = URI.encode_www_form({
160+
client_id: channel_id,
161+
id_token: id_token,
162+
nonce: nonce,
163+
user_id: user_id
164+
}.compact)
165+
headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
166+
post(oauth_endpoint, endpoint_path, payload, headers)
167+
end
168+
169+
# Verify access token v2.1
170+
#
171+
# @param access_token [String] access token
172+
#
173+
# @return [Net::HTTPResponse]
174+
def verify_access_token(access_token)
175+
payload = URI.encode_www_form(
176+
access_token: access_token
177+
)
178+
endpoint_path = "/oauth2/v2.1/verify?#{payload}"
179+
get(oauth_endpoint, endpoint_path)
180+
end
181+
148182
# Get all valid channel access token key IDs v2.1
149183
#
150184
# @param jwt [String]
@@ -161,6 +195,19 @@ def get_channel_access_token_key_ids_jwt(jwt)
161195
get(oauth_endpoint, endpoint_path, headers)
162196
end
163197

198+
# Get user profile by access token
199+
#
200+
# @param access_token [String] access token
201+
#
202+
# @return [Net::HTTPResponse]
203+
def get_profile_by_access_token(access_token)
204+
headers = {
205+
"Authorization" => "Bearer #{access_token}",
206+
}
207+
endpoint_path = "/v2/profile"
208+
get(oauth_endpoint, endpoint_path, headers)
209+
end
210+
164211
# Push messages to a user using user_id.
165212
#
166213
# @param user_id [String] User Id

spec/line/bot/client_channel_token_spec.rb

+69
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,38 @@
3232
}
3333
EOS
3434

35+
VERIFY_ID_TOKEN_CONTENT = <<"EOS"
36+
{
37+
"iss": "https://access.line.me",
38+
"sub": "U1234567890abcdef1234567890abcdef",
39+
"aud": "1234567890",
40+
"exp": 1504169092,
41+
"iat": 1504263657,
42+
"nonce": "0987654asdf",
43+
"amr": ["pwd"],
44+
"name": "Taro Line",
45+
"picture": "https://sample_line.me/aBcdefg123456",
46+
"email": "[email protected]"
47+
}
48+
EOS
49+
50+
VERIFY_ACCESS_TOKEN_CONTENT = <<"EOS"
51+
{
52+
"scope": "profile",
53+
"client_id": "1440057261",
54+
"expires_in": 2591659
55+
}
56+
EOS
57+
58+
PROFILE_BY_ACCESS_TOKEN_CONTENT = <<"EOS"
59+
{
60+
"userId": "U4af4980629...",
61+
"displayName": "Brown",
62+
"pictureUrl": "https://profile.line-scdn.net/abcdefghijklmn",
63+
"statusMessage": "Hello, LINE!"
64+
}
65+
EOS
66+
3567
describe Line::Bot::Client do
3668
def dummy_config
3769
{
@@ -112,4 +144,41 @@ def generate_client
112144

113145
expect(response).to be_a(Net::HTTPOK)
114146
end
147+
148+
it 'verifies ID token' do
149+
uri_template = Addressable::Template.new Line::Bot::API::DEFAULT_OAUTH_ENDPOINT + '/oauth2/v2.1/verify'
150+
stub_request(:post, uri_template)
151+
.with(body: { client_id: 'channel id', id_token: 'dummy_id_token', nonce: 'dummy_nonce'})
152+
.to_return { |request| {body: VERIFY_ID_TOKEN_CONTENT, status: 200} }
153+
154+
client = generate_client
155+
156+
response = client.verify_id_token('dummy_id_token', nonce: 'dummy_nonce')
157+
158+
expect(response).to be_a(Net::HTTPOK).and(have_attributes(body: VERIFY_ID_TOKEN_CONTENT))
159+
end
160+
161+
it 'verifies access token' do
162+
uri_template = Addressable::Template.new Line::Bot::API::DEFAULT_OAUTH_ENDPOINT + '/oauth2/v2.1/verify?access_token=dummy_access_token'
163+
stub_request(:get, uri_template).to_return { |request| {body: VERIFY_ACCESS_TOKEN_CONTENT, status: 200} }
164+
165+
client = generate_client
166+
167+
response = client.verify_access_token('dummy_access_token')
168+
169+
expect(response).to be_a(Net::HTTPOK).and(have_attributes(body: VERIFY_ACCESS_TOKEN_CONTENT))
170+
end
171+
172+
it 'gets profile by access token' do
173+
uri_template = Addressable::Template.new Line::Bot::API::DEFAULT_OAUTH_ENDPOINT + '/v2/profile'
174+
stub_request(:get, uri_template)
175+
.with(headers: { 'Authorization' => 'Bearer dummy_access_token'})
176+
.to_return { |request| {body: PROFILE_BY_ACCESS_TOKEN_CONTENT, status: 200} }
177+
178+
client = generate_client
179+
180+
response = client.get_profile_by_access_token('dummy_access_token')
181+
182+
expect(response).to be_a(Net::HTTPOK).and(have_attributes(body: PROFILE_BY_ACCESS_TOKEN_CONTENT))
183+
end
115184
end

0 commit comments

Comments
 (0)