Skip to content

Commit

Permalink
V1.1 (#1)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
eliasjpr authored Nov 7, 2021
1 parent 36a4700 commit 46b7728
Show file tree
Hide file tree
Showing 33 changed files with 179 additions and 175 deletions.
2 changes: 0 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ WORKDIR /opt/app
COPY . /opt/app
RUN shards install
RUN crystal build --release --static ./src/server.cr -o ./server
RUN crystal build --release --static ./taskfile.cr -o ./azu
CMD ["crystal", "spec"]

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /opt/app/server .
COPY --from=0 /opt/app/azu .
COPY --from=0 /opt/app/public ./public
CMD ["./server"]
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ At this moment Authority issues JWT OAuth 2.0 Access Tokens as default.
Grant Types

- [x] Authorization code grant
- [x] Client credentials grant
- [x] Implicit grant
- [x] Resource owner credentials grant
- [x] Client credentials grant
- [x] Refresh token grant
- [x] OpenID Connect
- [x] PKCE
- [ ] Device Code grant
- [ ] Token Introspection
- [ ] Token Revocation

Expand Down Expand Up @@ -105,7 +106,7 @@ docker-compose up server

## Contributing

1. Fork it (https://github.com/azutoolkit/authority/fork)
1. Fork it (<https://github.com/azutoolkit/authority/fork>)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
Expand Down
9 changes: 9 additions & 0 deletions db/migrations/1627600350__create_uuid.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class CreateUUID
include Clear::Migration

def change(direction)
direction.up do
execute %(CREATE EXTENSION IF NOT EXISTS "uuid-ossp";)
end
end
end
5 changes: 3 additions & 2 deletions db/migrations/1627760477__create_clients.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ class CreateClients

def change(direction)
direction.up do
create_table :clients do |t|
t.column :client_id, "uuid", null: false, index: true, unique: true
create_table :clients, id: :uuid do |t|
t.column :client_id, "uuid", index: true, unique: true, default: "uuid_generate_v4()"
t.column :name, "varchar(120)", null: false, index: true, unique: true
t.column :description, "varchar(2000)"
t.column :logo, "varchar(120)", null: false
t.column :client_secret, "varchar(80)", null: false
t.column :redirect_uri, "varchar(2000)", null: false
t.column :scopes, "varchar(4000)", null: false
Expand Down
4 changes: 2 additions & 2 deletions db/migrations/1627760814__create_users.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ class CreateUser

def change(direction)
direction.up do
create_table :users do |t|
create_table :users, id: :uuid do |t|
t.column :username, "varchar(80)", null: false, index: true, unique: true
t.column :password, "varchar(80)", null: false
t.column :encrypted_password, "varchar(80)", null: false
t.column :first_name, "varchar(80)", null: false
t.column :last_name, "varchar(80)", null: false
t.column :email, "varchar(80)", null: false
Expand Down
15 changes: 1 addition & 14 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,6 @@ services:
ports:
- 5432:5432

migrator:
build:
context: .
dockerfile: migrate.Dockerfile
container_name: migrator
working_dir: /root/
env_file:
- local.env
ports:
- "4000:4000"
depends_on:
- db

server:
build:
context: .
Expand All @@ -35,4 +22,4 @@ services:
ports:
- "4000:4000"
depends_on:
- migrator
- db
7 changes: 0 additions & 7 deletions migrate.Dockerfile

This file was deleted.

14 changes: 6 additions & 8 deletions public/templates/authorize.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
}
}
</style>


</head>

<body>
Expand All @@ -50,19 +48,19 @@
<input type="hidden" name="code_challenge" value="{{code_challenge}}" />
<input type="hidden" name="code_challenge_method" value="{{code_challenge_method}}" />
<div class="card">
<div class="card-header text-center mt-2">
<h3>Authorize {{client.name}}</h3>
</div>
<img src="{{client.logo}}" class="card-img-top">
<div class="card-body">
<h5 class="card-title">{{client.name}}</h5>
<p class="card-text">{{client.description}}</p>
</div>
<div class="card-footer">
<div class="d-grid gap-2 mt-2">
<button type="submit" id="approve" name="approve" class="authorize-btn btn btn-success btn-lg">Authorize
{{client.name}}</button>
<button type="submit" id="approve" name="approve" class="authorize-btn btn btn-success btn-lg">
Authorize {{client.name}}
</button>
<p class="text-center mt-1">
Authorizing will redirect to <strong>{{client.redirect_uri}}</strong>
Authorizing will redirect to
<strong>{{client.redirect_uri}}</strong>
</p>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion shard.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ shards:

authly:
git: https://github.com/azutoolkit/authly.git
version: 0.3+git.commit.732bf4dd11e4d0f1d4f0d17365f89042d04773d3
version: 1.1.1

azu:
git: https://github.com/azutoolkit/azu.git
Expand Down
2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dependencies:
branch: master
authly:
github: azutoolkit/authly
version: 1.0
version: 1.1.1

development_dependencies:
faker:
Expand Down
7 changes: 5 additions & 2 deletions spec/authorization_code_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ require "./spec_helper"
describe Authority do
describe "Authorization Code Flow" do
it "gets access token" do
user = create_owner
password = Faker::Internet.password
user = create_owner(password: password)
state = Random::Secure.hex
auth_url = OAUTH_CLIENT.get_authorize_uri(scope: "read", state: state)
code, expected_state = AuthorizationCodeFlux.flow(auth_url, user.username, user.password)

code, expected_state = AuthorizationCodeFlux.flow(
auth_url, user.username, password)

token = OAUTH_CLIENT.get_access_token_using_authorization_code(code)

Expand Down
12 changes: 6 additions & 6 deletions spec/helpers/clients_helper.cr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
def create_client(client_id, client_secret, redirect_uri)
client = Authority::Client.new({
Authority::Client.new({
client_id: client_id,
client_secret: client_secret,
redirect_uri: redirect_uri,
grant_types: "cleint_credentials",
scope: "read",
})

client.save!
name: Faker::Company.name,
description: Faker::Lorem.paragraph(2),
logo: Faker::Company.logo,
scopes: "read",
}).save!
end
5 changes: 3 additions & 2 deletions spec/resource_owner_credentials_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ require "./spec_helper"
describe Authority do
describe "Password Flow" do
it "gets access token" do
user = create_owner
password = Faker::Internet.password
user = create_owner(password: password)

token = OAUTH_CLIENT.get_access_token_using_resource_owner_credentials(
username: user.username, password: user.password, scope: "read"
username: user.username, password: password, scope: "read"
)

token.should be_a OAuth2::AccessToken::Bearer
Expand Down
5 changes: 3 additions & 2 deletions spec/session_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ describe Authority do

describe "Create" do
it "create a new session" do
password = Faker::Internet.password
session_flux = SessionFlux.new
user = create_owner
result = session_flux.create user.username, user.password
user = create_owner(password: password)
result = session_flux.create user.username, password
result.should be_a URI::Params
end
end
Expand Down
15 changes: 9 additions & 6 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ require "./helpers/**"
require "./flows/**"
require "../src/authority"

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

OAUTH_CLIENT = OAuth2::Client.new(
"localhost", CLIENT_ID, CLIENT_SECRET, port: 4000, scheme: "http",
redirect_uri: REDIRECT_URI, authorize_uri: "/authorize", token_uri: "/token")
"localhost",
CLIENT_ID,
CLIENT_SECRET,
port: 4000,
scheme: "http",
redirect_uri: REDIRECT_URI,
authorize_uri: "/authorize",
token_uri: "/token")

Clear::SQL.truncate("authorization_codes", cascade: true)
Clear::SQL.truncate("users", cascade: true)
Clear::SQL.truncate("clients", cascade: true)
puts "Creating Clien: #{CLIENT_ID} #{CLIENT_SECRET}"
create_client(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI)

Spec.before_each do
Clear::SQL.truncate("authorization_codes", cascade: true)
Clear::SQL.truncate("users", cascade: true)
end
24 changes: 13 additions & 11 deletions spec/token_spec.cr
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
require "./spec_helper"

describe "TokenSpec" do
password = Faker::Internet.password

describe "OpenID" do
it "returns id_token" do
user = create_owner
user = create_owner(password: password)

scope = "openid read"
code, code_verifier, expected_state = prepare_code_challenge_url(user.username, user.password, "S256", scope)
code, code_verifier, expected_state = prepare_code_challenge_url(
user.username, password, "S256", scope)

response = create_token_request(code, code_verifier, scope)
token = OAuth2::AccessToken::Bearer.from_json(response.body)
p response.body

id_token = token.extra.not_nil!["id_token"]
p Authly.config.secret_key
p id_token
id_token.should_not be_nil
end
end
Expand All @@ -28,8 +30,9 @@ describe "TokenSpec" do
describe "Create Token" do
describe "Method S256" do
it "creates access token" do
user = create_owner
code, code_verifier, expected_state = prepare_code_challenge_url(user.username, user.password, "S256")
user = create_owner(password: password)
code, code_verifier, expected_state = prepare_code_challenge_url(
user.username, password, "S256")

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

describe "Method PLAIN" do
it "creates access token" do
user = create_owner
code, code_verifier, expected_state = prepare_code_challenge_url(user.username, user.password, "plain")
user = create_owner(password: password)
code, code_verifier, expected_state = prepare_code_challenge_url(
user.username, password, "plain")

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

p token

response.status_message.should eq "OK"
token.should be_a OAuth2::AccessToken::Bearer
end
Expand Down
4 changes: 3 additions & 1 deletion src/authority.cr
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ end
require "./config/**"
require "./services/**"
require "./requests/**"
require "./providers/**"
require "./responses/**"
require "./models/**"
require "./entities/**"
require "./repositories/**"
require "./endpoints/**"
require "./config/**"
15 changes: 3 additions & 12 deletions src/config/authly.cr
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
# Configure
Authly.configure do |c|
# Secret Key for JWT Tokens
c.secret_key = "ExampleSecretKey"

# Refresh Token Time To Live
c.refresh_ttl = 1.hour

# Authorization Code Time To Live
c.code_ttl = 1.hour

# Access Token Time To Live
c.code_ttl = 3.minutes
c.access_ttl = 1.hour

# Using your own classes
c.owners = Authority::OwnerService.new
c.clients = Authority::ClientService.new
c.owners = Authority::OwnerProvider.new
c.clients = Authority::ClientProvider.new
end
5 changes: 4 additions & 1 deletion src/endpoints/access_token_create_endpoint.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ module Authority
post "/token"

def call : AccessTokenCreateResponse
access_token = AccessTokenService.access_token *credentials, access_token_create_request
AccessTokenCreateResponse.new access_token
end

private def access_token : Authly::AccessToken
AccessTokenService.access_token *credentials, access_token_create_request
end

private def credentials
value = header[AUTH]
client_id, client_secret = Base64.decode_string(value[BASIC.size + 1..-1]).split(":")
Expand Down
2 changes: 1 addition & 1 deletion src/endpoints/session_create_endpoint.cr
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module Authority
end

private def authorized?
OwnerService.new.authorized?(
Authly.owners.authorized?(
session_create_request.username,
session_create_request.password
)
Expand Down
12 changes: 8 additions & 4 deletions src/models/client.cr → src/entities/client.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ module Authority

self.table = "clients"

primary_key
column client_id : String
primary_key :id, type: :uuid

column name : String
column client_id : UUID
column client_secret : String
column redirect_uri : String
column grant_types : String
column scope : String
column description : String
column name : String
column logo : String
column scopes : String

timestamps
end
Expand Down
Loading

0 comments on commit 46b7728

Please sign in to comment.