Skip to content

Commit 4884be6

Browse files
authored
[v1.4.v1] Adds Missing Device Code Flow Spec (#6)
The Device Code grant type is used by browserless or input-constrained devices in the device flow to exchange a previously obtained device code for an access token. The Device Code grant type value is urn:ietf:params:oauth:grant-type:device_code.
1 parent 22ac908 commit 4884be6

File tree

7 files changed

+91
-20
lines changed

7 files changed

+91
-20
lines changed

.github/workflows/spec.yml

-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ jobs:
5555
run: crystal spec
5656
env:
5757
BASE_URL: http://localhost:4000
58-
ACTIVATE_URL: http://localhost:4000/activate
5958
DEVICE_CODE_TTL: 300
6059
SECRET_KEY: secret_key
6160
REFRESH_TTL: 60

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Authority
22

3+
<<<<<<< HEAD
34
[![Test](https://github.com/azutoolkit/authority/actions/workflows/spec.yml/badge.svg)](https://github.com/azutoolkit/authority/actions/workflows/spec.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/c19b4551de9f43c2b79664af5908f033)](https://www.codacy.com/gh/azutoolkit/authority/dashboard?utm_source=github.com&utm_medium=referral&utm_content=azutoolkit/authority&utm_campaign=Badge_Grade) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/azutoolkit/authority?label=shard) [![documentation](https://img.shields.io/badge/documentation-authority-brightgreen)](https://azutopia.gitbook.io/authority)
5+
=======
6+
[![Test](https://github.com/azutoolkit/authority/actions/workflows/spec.yml/badge.svg)](https://github.com/azutoolkit/authority/actions/workflows/spec.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/c19b4551de9f43c2b79664af5908f033)](https://www.codacy.com/gh/azutoolkit/authority/dashboard?utm_source=github.com&utm_medium=referral&utm_content=azutoolkit/authority&utm_campaign=Badge_Grade) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/azutoolkit/authority?label=shard)
7+
>>>>>>> 25a95a7 ([v1.4] Adds Device Code Flow)
48
59
![logo](https://user-images.githubusercontent.com/1685772/141647649-241cff93-a5dc-4e6a-9695-ff4b9e6a51d4.png)
610

@@ -111,7 +115,10 @@ TEMPLATES_PATH="./public/templates"
111115
ERROR_TEMPLATE
112116
SESSION_KEY="session_id"
113117
BASE_URL=http://localhost:4000
118+
<<<<<<< HEAD
114119
ACTIVATE_URL=http://localhost:4000/activate
120+
=======
121+
>>>>>>> 25a95a7 ([v1.4] Adds Device Code Flow)
115122
DEVICE_CODE_TTL=300
116123
SSL_CERT=
117124
SSL_KEY=

local.env

-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ SECRET_KEY=secret_key
1010
REFRESH_TTL=60
1111
CODE_TTL=5
1212
ACCESS_TOKEN_TTL=60
13-
TEMPLATES_PATH="./public/templates"
1413
ERROR_TEMPLATE
1514
SESSION_KEY="session_id"
1615
BASE_URL=http://localhost:4000
17-
ACTIVATE_URL=http://localhost:4000/activate
1816
DEVICE_CODE_TTL=300

spec/device_code_spec.cr

+40-14
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,44 @@
11
require "./spec_helper"
22

33
describe Authority do
4-
# http_client = OAUTH_CLIENT.http_client
5-
# client = create_client
6-
# describe "Device Code Flow" do
7-
# it "gets device code" do
8-
# response = http_client.post("/device/code?client_id=#{client.client_id}")
9-
10-
# response.status_code.should eq 201
11-
# # Make request to "/device/code"
12-
# # Response should be
13-
# end
14-
15-
# it "gets device token" do
16-
# end
17-
# end
4+
http_client = OAUTH_CLIENT.http_client
5+
6+
describe "Device Code Flow" do
7+
it "gets device code" do
8+
password = Faker::Internet.password
9+
owner = create_owner(password: password)
10+
username = owner.username
11+
response = http_client.post("/device/code?client_id=#{CLIENT_ID}&scope=read")
12+
13+
response.status_code.should eq 201
14+
json = JSON.parse(response.body)
15+
16+
json["verification_uri"].should eq "http://localhost:4000/activate"
17+
18+
verification_url = json["verification_full"].as_s
19+
user_code = json["user_code"].as_s
20+
device_code = json["device_code"].as_s
21+
22+
url = DeviceCodeFlux.flow(verification_url, username, password, user_code, "allowed")
23+
24+
url.should eq "http://localhost:4000/activate"
25+
26+
response = http_client.post("/device/token", form: {
27+
"grant_type" => "urn:ietf:params:oauth:grant-type:device_code",
28+
"code" => device_code,
29+
"client_id" => CLIENT_ID,
30+
31+
})
32+
33+
access_token = JSON.parse(response.body)
34+
35+
access_token["access_token"].as_s.should_not be_empty
36+
access_token["token_type"].as_s.should eq "Bearer"
37+
access_token["refresh_token"].as_s.should_not be_empty
38+
access_token["access_token"].as_s.should_not be_empty
39+
end
40+
41+
it "gets device token" do
42+
end
43+
end
1844
end

spec/flows/device_code_flux.cr

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
require "flux"
2+
require "uri"
3+
4+
class DeviceCodeFlux < Flux
5+
def self.flow(url, username, password, user_code, verification)
6+
new(url, username, password, user_code, verification).call
7+
end
8+
9+
def initialize(
10+
@url : String,
11+
@username : String,
12+
@password : String,
13+
@user_code : String,
14+
@verification : String
15+
)
16+
options = Marionette.firefox_options(args: ["-headless"])
17+
super(Marionette::Browser::Firefox, options)
18+
end
19+
20+
def call
21+
step do
22+
visit @url
23+
24+
fill "#username", @username, by: :css
25+
fill "#password", @password, by: :css
26+
submit "#signin", by: :css
27+
28+
sleep 1.seconds
29+
30+
submit "[value=#{@verification}]", by: :css
31+
32+
sleep 1.seconds
33+
34+
return current_url
35+
end
36+
end
37+
end

spec/spec_helper.cr

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ Clear::SQL.truncate("owners", cascade: true)
2828
Clear::SQL.truncate("clients", cascade: true)
2929
create_client(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI)
3030

31-
process = Process.new("./bin/server", env: ENV.to_h)
31+
process = Process.new(
32+
"./bin/server",
33+
env: ENV.to_h,
34+
output: Process::Redirect::Inherit,
35+
error: Process::Redirect::Inherit)
3236
# Wait for process to start
3337
sleep 1.seconds
3438

src/config/authority.cr

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ module Authority
55

66
SESSION_KEY = ENV.fetch "SESSION_KEY", "session_id"
77
BASE_URL = ENV.fetch "BASE_URL", "http://localhost:4000"
8-
ACTIVATE_URL = ENV.fetch "ACTIVATE_URL", "http://localhost:4000/activate"
8+
ACTIVATE_URL = "#{BASE_URL}/activate"
99
DEVICE_CODE_TTL = ENV.fetch("DEVICE_CODE_TTL", "300").to_i
1010

1111
configure do |c|
12-
# To Server static content
12+
c.templates.path = "./public/templates"
1313
c.router.get "/*", Handler::Static.new
1414
end
1515
end

0 commit comments

Comments
 (0)