Skip to content

Commit 46b7728

Browse files
authored
V1.1 (#1)
* v1.1 Fixes Entities Why Adds encrypted passwords and basic User and Clients models. These changes prepare the application to allow User and Clients (Apps) registrations. - Renames Models to Entities - Adds Repositories for Clients and Users - Uses UUID for table primary keys - Uses UUID for Client ID - Adds Name and Logo to Clients table - Removes State and Scope from the `/token` request
1 parent 36a4700 commit 46b7728

33 files changed

+179
-175
lines changed

Dockerfile

-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ WORKDIR /opt/app
44
COPY . /opt/app
55
RUN shards install
66
RUN crystal build --release --static ./src/server.cr -o ./server
7-
RUN crystal build --release --static ./taskfile.cr -o ./azu
87
CMD ["crystal", "spec"]
98

109
FROM alpine:latest
1110
RUN apk --no-cache add ca-certificates
1211
WORKDIR /root/
1312
COPY --from=0 /opt/app/server .
14-
COPY --from=0 /opt/app/azu .
1513
COPY --from=0 /opt/app/public ./public
1614
CMD ["./server"]

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ At this moment Authority issues JWT OAuth 2.0 Access Tokens as default.
3030
Grant Types
3131

3232
- [x] Authorization code grant
33+
- [x] Client credentials grant
3334
- [x] Implicit grant
3435
- [x] Resource owner credentials grant
35-
- [x] Client credentials grant
3636
- [x] Refresh token grant
3737
- [x] OpenID Connect
3838
- [x] PKCE
39+
- [ ] Device Code grant
3940
- [ ] Token Introspection
4041
- [ ] Token Revocation
4142

@@ -105,7 +106,7 @@ docker-compose up server
105106

106107
## Contributing
107108

108-
1. Fork it (https://github.com/azutoolkit/authority/fork)
109+
1. Fork it (<https://github.com/azutoolkit/authority/fork>)
109110
2. Create your feature branch (`git checkout -b my-new-feature`)
110111
3. Commit your changes (`git commit -am 'Add some feature'`)
111112
4. Push to the branch (`git push origin my-new-feature`)
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class CreateUUID
2+
include Clear::Migration
3+
4+
def change(direction)
5+
direction.up do
6+
execute %(CREATE EXTENSION IF NOT EXISTS "uuid-ossp";)
7+
end
8+
end
9+
end

db/migrations/1627760477__create_clients.cr

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ class CreateClients
33

44
def change(direction)
55
direction.up do
6-
create_table :clients do |t|
7-
t.column :client_id, "uuid", null: false, index: true, unique: true
6+
create_table :clients, id: :uuid do |t|
7+
t.column :client_id, "uuid", index: true, unique: true, default: "uuid_generate_v4()"
88
t.column :name, "varchar(120)", null: false, index: true, unique: true
99
t.column :description, "varchar(2000)"
10+
t.column :logo, "varchar(120)", null: false
1011
t.column :client_secret, "varchar(80)", null: false
1112
t.column :redirect_uri, "varchar(2000)", null: false
1213
t.column :scopes, "varchar(4000)", null: false

db/migrations/1627760814__create_users.cr

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ class CreateUser
33

44
def change(direction)
55
direction.up do
6-
create_table :users do |t|
6+
create_table :users, id: :uuid do |t|
77
t.column :username, "varchar(80)", null: false, index: true, unique: true
8-
t.column :password, "varchar(80)", null: false
8+
t.column :encrypted_password, "varchar(80)", null: false
99
t.column :first_name, "varchar(80)", null: false
1010
t.column :last_name, "varchar(80)", null: false
1111
t.column :email, "varchar(80)", null: false

docker-compose.yml

+1-14
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,6 @@ services:
1010
ports:
1111
- 5432:5432
1212

13-
migrator:
14-
build:
15-
context: .
16-
dockerfile: migrate.Dockerfile
17-
container_name: migrator
18-
working_dir: /root/
19-
env_file:
20-
- local.env
21-
ports:
22-
- "4000:4000"
23-
depends_on:
24-
- db
25-
2613
server:
2714
build:
2815
context: .
@@ -35,4 +22,4 @@ services:
3522
ports:
3623
- "4000:4000"
3724
depends_on:
38-
- migrator
25+
- db

migrate.Dockerfile

-7
This file was deleted.

public/templates/authorize.html

+6-8
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
}
3434
}
3535
</style>
36-
37-
3836
</head>
3937

4038
<body>
@@ -50,19 +48,19 @@
5048
<input type="hidden" name="code_challenge" value="{{code_challenge}}" />
5149
<input type="hidden" name="code_challenge_method" value="{{code_challenge_method}}" />
5250
<div class="card">
53-
<div class="card-header text-center mt-2">
54-
<h3>Authorize {{client.name}}</h3>
55-
</div>
51+
<img src="{{client.logo}}" class="card-img-top">
5652
<div class="card-body">
5753
<h5 class="card-title">{{client.name}}</h5>
5854
<p class="card-text">{{client.description}}</p>
5955
</div>
6056
<div class="card-footer">
6157
<div class="d-grid gap-2 mt-2">
62-
<button type="submit" id="approve" name="approve" class="authorize-btn btn btn-success btn-lg">Authorize
63-
{{client.name}}</button>
58+
<button type="submit" id="approve" name="approve" class="authorize-btn btn btn-success btn-lg">
59+
Authorize {{client.name}}
60+
</button>
6461
<p class="text-center mt-1">
65-
Authorizing will redirect to <strong>{{client.redirect_uri}}</strong>
62+
Authorizing will redirect to
63+
<strong>{{client.redirect_uri}}</strong>
6664
</p>
6765
</div>
6866
</div>

shard.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ shards:
77

88
authly:
99
git: https://github.com/azutoolkit/authly.git
10-
version: 0.3+git.commit.732bf4dd11e4d0f1d4f0d17365f89042d04773d3
10+
version: 1.1.1
1111

1212
azu:
1313
git: https://github.com/azutoolkit/azu.git

shard.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ dependencies:
2020
branch: master
2121
authly:
2222
github: azutoolkit/authly
23-
version: 1.0
23+
version: 1.1.1
2424

2525
development_dependencies:
2626
faker:

spec/authorization_code_spec.cr

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ require "./spec_helper"
33
describe Authority do
44
describe "Authorization Code Flow" do
55
it "gets access token" do
6-
user = create_owner
6+
password = Faker::Internet.password
7+
user = create_owner(password: password)
78
state = Random::Secure.hex
89
auth_url = OAUTH_CLIENT.get_authorize_uri(scope: "read", state: state)
9-
code, expected_state = AuthorizationCodeFlux.flow(auth_url, user.username, user.password)
10+
11+
code, expected_state = AuthorizationCodeFlux.flow(
12+
auth_url, user.username, password)
1013

1114
token = OAUTH_CLIENT.get_access_token_using_authorization_code(code)
1215

spec/helpers/clients_helper.cr

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
def create_client(client_id, client_secret, redirect_uri)
2-
client = Authority::Client.new({
2+
Authority::Client.new({
33
client_id: client_id,
44
client_secret: client_secret,
55
redirect_uri: redirect_uri,
6-
grant_types: "cleint_credentials",
7-
scope: "read",
8-
})
9-
10-
client.save!
6+
name: Faker::Company.name,
7+
description: Faker::Lorem.paragraph(2),
8+
logo: Faker::Company.logo,
9+
scopes: "read",
10+
}).save!
1111
end

spec/resource_owner_credentials_spec.cr

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ require "./spec_helper"
33
describe Authority do
44
describe "Password Flow" do
55
it "gets access token" do
6-
user = create_owner
6+
password = Faker::Internet.password
7+
user = create_owner(password: password)
78

89
token = OAUTH_CLIENT.get_access_token_using_resource_owner_credentials(
9-
username: user.username, password: user.password, scope: "read"
10+
username: user.username, password: password, scope: "read"
1011
)
1112

1213
token.should be_a OAuth2::AccessToken::Bearer

spec/session_spec.cr

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ describe Authority do
1515

1616
describe "Create" do
1717
it "create a new session" do
18+
password = Faker::Internet.password
1819
session_flux = SessionFlux.new
19-
user = create_owner
20-
result = session_flux.create user.username, user.password
20+
user = create_owner(password: password)
21+
result = session_flux.create user.username, password
2122
result.should be_a URI::Params
2223
end
2324
end

spec/spec_helper.cr

+9-6
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,24 @@ require "./helpers/**"
88
require "./flows/**"
99
require "../src/authority"
1010

11-
CLIENT_ID = Faker::Internet.user_name
11+
CLIENT_ID = UUID.random.to_s
1212
CLIENT_SECRET = Faker::Internet.password(32, 32)
1313
REDIRECT_URI = "http://www.example.com/callback"
1414

1515
OAUTH_CLIENT = OAuth2::Client.new(
16-
"localhost", CLIENT_ID, CLIENT_SECRET, port: 4000, scheme: "http",
17-
redirect_uri: REDIRECT_URI, authorize_uri: "/authorize", token_uri: "/token")
16+
"localhost",
17+
CLIENT_ID,
18+
CLIENT_SECRET,
19+
port: 4000,
20+
scheme: "http",
21+
redirect_uri: REDIRECT_URI,
22+
authorize_uri: "/authorize",
23+
token_uri: "/token")
1824

19-
Clear::SQL.truncate("authorization_codes", cascade: true)
2025
Clear::SQL.truncate("users", cascade: true)
2126
Clear::SQL.truncate("clients", cascade: true)
22-
puts "Creating Clien: #{CLIENT_ID} #{CLIENT_SECRET}"
2327
create_client(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI)
2428

2529
Spec.before_each do
26-
Clear::SQL.truncate("authorization_codes", cascade: true)
2730
Clear::SQL.truncate("users", cascade: true)
2831
end

spec/token_spec.cr

+13-11
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
require "./spec_helper"
22

33
describe "TokenSpec" do
4+
password = Faker::Internet.password
5+
46
describe "OpenID" do
57
it "returns id_token" do
6-
user = create_owner
8+
user = create_owner(password: password)
9+
710
scope = "openid read"
8-
code, code_verifier, expected_state = prepare_code_challenge_url(user.username, user.password, "S256", scope)
11+
code, code_verifier, expected_state = prepare_code_challenge_url(
12+
user.username, password, "S256", scope)
913

1014
response = create_token_request(code, code_verifier, scope)
1115
token = OAuth2::AccessToken::Bearer.from_json(response.body)
12-
p response.body
16+
1317
id_token = token.extra.not_nil!["id_token"]
14-
p Authly.config.secret_key
15-
p id_token
1618
id_token.should_not be_nil
1719
end
1820
end
@@ -28,8 +30,9 @@ describe "TokenSpec" do
2830
describe "Create Token" do
2931
describe "Method S256" do
3032
it "creates access token" do
31-
user = create_owner
32-
code, code_verifier, expected_state = prepare_code_challenge_url(user.username, user.password, "S256")
33+
user = create_owner(password: password)
34+
code, code_verifier, expected_state = prepare_code_challenge_url(
35+
user.username, password, "S256")
3336

3437
response = create_token_request(code, code_verifier)
3538
token = OAuth2::AccessToken::Bearer.from_json(response.body)
@@ -41,14 +44,13 @@ describe "TokenSpec" do
4144

4245
describe "Method PLAIN" do
4346
it "creates access token" do
44-
user = create_owner
45-
code, code_verifier, expected_state = prepare_code_challenge_url(user.username, user.password, "plain")
47+
user = create_owner(password: password)
48+
code, code_verifier, expected_state = prepare_code_challenge_url(
49+
user.username, password, "plain")
4650

4751
response = create_token_request(code, code_verifier)
4852
token = OAuth2::AccessToken::Bearer.from_json(response.body)
4953

50-
p token
51-
5254
response.status_message.should eq "OK"
5355
token.should be_a OAuth2::AccessToken::Bearer
5456
end

src/authority.cr

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ end
2222
require "./config/**"
2323
require "./services/**"
2424
require "./requests/**"
25+
require "./providers/**"
2526
require "./responses/**"
26-
require "./models/**"
27+
require "./entities/**"
28+
require "./repositories/**"
2729
require "./endpoints/**"
2830
require "./config/**"

src/config/authly.cr

+3-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
11
# Configure
22
Authly.configure do |c|
3-
# Secret Key for JWT Tokens
43
c.secret_key = "ExampleSecretKey"
5-
6-
# Refresh Token Time To Live
74
c.refresh_ttl = 1.hour
8-
9-
# Authorization Code Time To Live
10-
c.code_ttl = 1.hour
11-
12-
# Access Token Time To Live
5+
c.code_ttl = 3.minutes
136
c.access_ttl = 1.hour
14-
15-
# Using your own classes
16-
c.owners = Authority::OwnerService.new
17-
c.clients = Authority::ClientService.new
7+
c.owners = Authority::OwnerProvider.new
8+
c.clients = Authority::ClientProvider.new
189
end

src/endpoints/access_token_create_endpoint.cr

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ module Authority
99
post "/token"
1010

1111
def call : AccessTokenCreateResponse
12-
access_token = AccessTokenService.access_token *credentials, access_token_create_request
1312
AccessTokenCreateResponse.new access_token
1413
end
1514

15+
private def access_token : Authly::AccessToken
16+
AccessTokenService.access_token *credentials, access_token_create_request
17+
end
18+
1619
private def credentials
1720
value = header[AUTH]
1821
client_id, client_secret = Base64.decode_string(value[BASIC.size + 1..-1]).split(":")

src/endpoints/session_create_endpoint.cr

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ module Authority
4040
end
4141

4242
private def authorized?
43-
OwnerService.new.authorized?(
43+
Authly.owners.authorized?(
4444
session_create_request.username,
4545
session_create_request.password
4646
)

src/models/client.cr renamed to src/entities/client.cr

+8-4
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@ module Authority
55

66
self.table = "clients"
77

8-
primary_key
9-
column client_id : String
8+
primary_key :id, type: :uuid
9+
10+
column name : String
11+
column client_id : UUID
1012
column client_secret : String
1113
column redirect_uri : String
12-
column grant_types : String
13-
column scope : String
14+
column description : String
15+
column name : String
16+
column logo : String
17+
column scopes : String
1418

1519
timestamps
1620
end

0 commit comments

Comments
 (0)