Skip to content

Commit 5ebfa00

Browse files
authored
Merge pull request #190 from line/feature/channel_access_token_v21
Support channel access token v2.1
2 parents e5c3488 + 5cc2f93 commit 5ebfa00

File tree

3 files changed

+125
-0
lines changed

3 files changed

+125
-0
lines changed

Diff for: lib/line/bot/api.rb

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
module Line
1818
module Bot
1919
module API
20+
DEFAULT_OAUTH_ENDPOINT = "https://api.line.me"
2021
DEFAULT_ENDPOINT = "https://api.line.me/v2"
2122
DEFAULT_BLOB_ENDPOINT = "https://api-data.line.me/v2"
2223
DEFAULT_LIFF_ENDPOINT = "https://api.line.me/liff/v1"

Diff for: lib/line/bot/client.rb

+61
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ def endpoint
5555
@endpoint ||= API::DEFAULT_ENDPOINT
5656
end
5757

58+
def oauth_endpoint
59+
@oauth_endpoint ||= API::DEFAULT_OAUTH_ENDPOINT
60+
end
61+
5862
def blob_endpoint
5963
return @blob_endpoint if @blob_endpoint
6064

@@ -106,6 +110,63 @@ def revoke_channel_token(access_token)
106110
post(endpoint, endpoint_path, payload, headers)
107111
end
108112

113+
# Issue channel access token v2.1
114+
#
115+
# @param jwt [String]
116+
#
117+
# @return [Net::HTTPResponse]
118+
def issue_channel_access_token_jwt(jwt)
119+
channel_id_required
120+
channel_secret_required
121+
122+
endpoint_path = '/oauth2/v2.1/token'
123+
payload = URI.encode_www_form(
124+
grant_type: 'client_credentials',
125+
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
126+
client_assertion: jwt
127+
)
128+
headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
129+
post(oauth_endpoint, endpoint_path, payload, headers)
130+
end
131+
132+
# Revoke channel access token v2.1
133+
#
134+
# @param access_token [String]
135+
#
136+
# @return [Net::HTTPResponse]
137+
def revoke_channel_access_token_jwt(access_token)
138+
channel_id_required
139+
channel_secret_required
140+
141+
endpoint_path = '/oauth2/v2.1/revoke'
142+
payload = URI.encode_www_form(
143+
client_id: channel_id,
144+
client_secret: channel_secret,
145+
access_token: access_token
146+
)
147+
headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
148+
post(oauth_endpoint, endpoint_path, payload, headers)
149+
end
150+
151+
# Get all valid channel access token key IDs v2.1
152+
#
153+
# @param jwt [String]
154+
#
155+
# @return [Net::HTTPResponse]
156+
def get_channel_access_token_key_ids_jwt(jwt)
157+
channel_id_required
158+
channel_secret_required
159+
160+
payload = URI.encode_www_form(
161+
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
162+
client_assertion: jwt
163+
)
164+
endpoint_path = "/oauth2/v2.1/tokens/kid?#{payload}"
165+
166+
headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
167+
get(oauth_endpoint, endpoint_path, headers)
168+
end
169+
109170
# Push messages to a user using user_id.
110171
#
111172
# @param user_id [String] User Id

Diff for: spec/line/bot/client_channel_token_spec.rb

+63
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,28 @@
1010
}
1111
EOS
1212

13+
ISSUE_CHANNEL_ACCESS_TOKEN_JWT_CONTENT = <<"EOS"
14+
{
15+
"access_token": "eyJhbGciOiJIUzxxxxxx",
16+
"token_type": "Bearer",
17+
"expires_in": 2592000,
18+
"key_id": "sDTOzw5wIfxxxxPEzcmeQA"
19+
}
20+
EOS
21+
22+
GET_CHANNEL_ACCESS_TOKEN_KEY_IDS_JWT_CONTENT = <<"EOS"
23+
{
24+
"key_ids": [
25+
"U_gdnFYKTWRxxxxDVZexGg",
26+
"sDTOzw5wIfWxxxxzcmeQA",
27+
"73hDyp3PxGfxxxxD6U5qYA",
28+
"FHGanaP79smDxxxxyPrVw",
29+
"CguB-0kxxxxdSM3A5Q_UtQ",
30+
"G82YP96jhHwyKSxxxx7IFA"
31+
]
32+
}
33+
EOS
34+
1335
describe Line::Bot::Client do
1436
def dummy_config
1537
{
@@ -49,4 +71,45 @@ def generate_client
4971

5072
expect(response).to be_a(Net::HTTPOK)
5173
end
74+
75+
it 'issues an oauth access token v2.1' do
76+
uri_template = Addressable::Template.new Line::Bot::API::DEFAULT_OAUTH_ENDPOINT + '/oauth2/v2.1/token'
77+
stub_request(:post, uri_template).to_return { |request| {body: ISSUE_CHANNEL_ACCESS_TOKEN_JWT_CONTENT, status: 200} }
78+
79+
client = generate_client
80+
response = client.issue_channel_access_token_jwt('jwt_string')
81+
82+
expect(response).to be_a(Net::HTTPOK)
83+
result = JSON.parse(response.body)
84+
expect(result['access_token']).to eq 'eyJhbGciOiJIUzxxxxxx'
85+
expect(result['expires_in']).to eq 2592000
86+
expect(result['token_type']).to eq 'Bearer'
87+
expect(result['key_id']).to eq 'sDTOzw5wIfxxxxPEzcmeQA'
88+
end
89+
90+
it 'revokes the oauth access token v2.1' do
91+
uri_template = Addressable::Template.new Line::Bot::API::DEFAULT_OAUTH_ENDPOINT + '/oauth2/v2.1/revoke'
92+
stub_request(:post, uri_template).to_return { |request| {body: '', status: 200} }
93+
94+
client = generate_client
95+
96+
response = client.revoke_channel_access_token_jwt('sDTOzw5wIfxxxxPEzcmeQA')
97+
98+
expect(response).to be_a(Net::HTTPOK)
99+
end
100+
101+
it 'get all valid channel access token key ids v2.1' do
102+
client_assertion = 'jwt_string'
103+
client_assertion_type = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
104+
105+
uri_template = Addressable::Template.new Line::Bot::API::DEFAULT_OAUTH_ENDPOINT +
106+
"/oauth2/v2.1/tokens/kid?client_assertion=#{client_assertion}&client_assertion_type=#{client_assertion_type}"
107+
stub_request(:any, uri_template).to_return { |request| {body: GET_CHANNEL_ACCESS_TOKEN_KEY_IDS_JWT_CONTENT, status: 200} }
108+
109+
client = generate_client
110+
111+
response = client.get_channel_access_token_key_ids_jwt('jwt_string')
112+
113+
expect(response).to be_a(Net::HTTPOK)
114+
end
52115
end

0 commit comments

Comments
 (0)