Skip to content
azutoolkitPublic

About

OAuth2 Provider Library - Authly is an OAuth2 Library for creating Authorization Servers that follows OAuth2 authorization mechanisms.

Topics

Resources

Stars

34 stars

Watchers

2 watching

Forks

Repository files navigation

Authly

Codacy Badge Crystal CI

Authly Logo

A standards-compliant OAuth 2.0, OpenID Connect, and Resource Server security shard for Crystal

Authly is a zero-framework OAuth 2.0 and OpenID Connect library built directly on Crystal's standard HTTP::Handler. Mount it as an Authorization Server (Authly::Handler), protect your API routes as a Resource Server (Authly::Handler::Bearer and Authly::Handler::RequireScope), or run both together in the same process or across microservices.


Key Capabilities

Area Supported Standards & Features
OAuth 2.0 Grants Authorization Code (authorization_code), Client Credentials (client_credentials), Refresh Token (refresh_token with rotation & family reuse detection), Device Authorization (urn:ietf:params:oauth:grant-type:device_code), Cryptographic Challenge-Response (challenge_response / urn:ietf:params:oauth:grant-type:challenge), Social Sign-In (social / urn:ietf:params:oauth:grant-type:social), Password (password), Implicit (implicit)
Social Sign-In Built-in adapters for Google, GitHub, Apple, Microsoft / Entra ID, Discord, and generic OAuth2/OIDC providers (GET /oauth/social/:provider, GET|POST /oauth/social/:provider/callback, and POST /oauth/token)
OpenID Connect id_token issuance (nonce, acr, amr, auth_time), GET /oauth/userinfo, OIDC Discovery (/.well-known/openid-configuration), OAuth 2.0 Authorization Server Metadata (/.well-known/oauth-authorization-server — RFC 8414), JWKS (/.well-known/jwks.json — RFC 7517)
FAPI & Advanced Security PKCE with S256 enforcement (RFC 7636), Pushed Authorization Requests (POST /oauth/par — RFC 9126), JWT Secured Authorization Response Mode (JARM — RFC 9101), Certificate-Bound Access Tokens (cnf.x5t#S256 mTLS — RFC 8705), Dynamic Client Registration (POST /oauth/register — RFC 7591)
Token Lifecycle :jwt (HS*, RS*, ES*) and :opaque token strategies, asymmetric key rotation (signing_key_id + verification_keys), Token Introspection (POST /oauth/introspect — RFC 7662), Token Revocation (POST /oauth/revoke — RFC 7009), host-controlled IssuanceOptions
Resource Server Protection Authly::Handler::Bearer (RFC 6750 verification, audience checking, context.authly_claims!) and Authly::Handler::RequireScope (route-level all / any scope enforcement)

Installation

Add authly to your shard.yml:

dependencies:
  authly:
    github: azutoolkit/authly

Then run:

shards install

Requirements: Crystal >= 1.22.0


Quick Start

Create a working OAuth 2.0 Authorization Server and protected Resource API in a single file:

require "http/server"
require "json"
require "authly"

Authly.configure do |config|
  config.issuer = "http://localhost:8080"
  config.base_url = "http://localhost:8080"
  config.secret_key = ENV.fetch("AUTHLY_SECRET_KEY", "dev-secret-change-in-production-32b")
  config.algorithm = JWT::Algorithm::HS256
  config.token_strategy = :jwt
end

# Register a client for machine-to-machine authentication
Authly.clients << Authly::Client.new(
  name: "Billing Service",
  secret: "billing-secret-123",
  redirect_uri: "http://localhost:8080/callback",
  id: "billing-service",
  scopes: "invoices:read invoices:write",
  allowed_grant_types: ["client_credentials"]
)

app = HTTP::Handler::HandlerProc.new do |context|
  case context.request.path
  when "/health"
    context.response.content_type = "application/json"
    context.response.print({status: "ok"}.to_json)
  when "/api/invoices"
    claims = context.authly_claims!
    context.response.content_type = "application/json"
    context.response.print({
      client_id: claims.client_id,
      scopes:    claims.scopes,
      invoices:  [{id: "inv_01", amount_cents: 4500}],
    }.to_json)
  else
    context.response.status = HTTP::Status::NOT_FOUND
  end
end

server = HTTP::Server.new([
  Authly::Handler.new,
  Authly::Handler::Bearer.new(
    realm: "api",
    except_exact: ["/health"],
    except: ["/oauth", "/.well-known"]
  ),
  Authly::Handler::RequireScope.all("invoices:read", prefixes: ["/api/invoices"]),
  app,
] of HTTP::Handler)

address = server.bind_tcp("127.0.0.1", 8080)
puts "Listening on http://#{address}"
server.listen

Issue a token and call the protected API:

# 1. Request an access token
TOKEN=$(curl -s -X POST http://127.0.0.1:8080/oauth/token \
  -u "billing-service:billing-secret-123" \
  -d "grant_type=client_credentials&scope=invoices:read" | jq -r .access_token)

# 2. Call the protected endpoint
curl -i http://127.0.0.1:8080/api/invoices \
  -H "Authorization: Bearer $TOKEN"

Documentation

Full documentation is available in docs/:

Step-by-step guides that walk you through building working applications from scratch:

Practical recipes for configuring production features:

Complete technical specifications:

Architectural overviews and security design:


HTTP Endpoints Overview

Authly::Handler mounts the following endpoints out of the box:

Method(s) Path Purpose
GET, POST /oauth/authorize Interactive authorization & user consent (RFC 6749, OIDC, PKCE, PAR, JARM)
POST /oauth/token Token issuance & refresh (authorization_code, client_credentials, refresh_token, password, device_code, challenge_response)
POST /oauth/par Pushed Authorization Requests (RFC 9126)
POST /oauth/challenge Cryptographic Challenge-Response nonce issuance
GET /oauth/social/:provider Social Sign-In redirect initiation (PKCE + signed CSRF state)
GET, POST /oauth/social/:provider/callback Social Sign-In provider callback
POST /oauth/device/code Device Authorization code issuance (RFC 8628)
GET, POST /device End-user device code verification page (RFC 8628)
POST /oauth/introspect, /introspect Token Introspection (RFC 7662)
POST /oauth/revoke, /revoke Token Revocation (RFC 7009)
GET /oauth/userinfo OpenID Connect UserInfo claims
POST /oauth/register Dynamic Client Registration (RFC 7591)
GET, HEAD /.well-known/openid-configuration OpenID Connect Discovery 1.0 metadata
GET, HEAD /.well-known/oauth-authorization-server OAuth 2.0 Authorization Server Metadata (RFC 8414)
GET, HEAD /.well-known/jwks.json JSON Web Key Set (RFC 7517)

Development & Contributing

Contributions are welcome! To work on Authly locally:

# Install dependencies
shards install

# Run the test suite
crystal spec

# Check formatting
crystal tool format --check src spec

# Run static code analysis
crystal run lib/ameba/src/cli.cr
  1. Fork the repository (https://github.com/azutoolkit/authly/fork)
  2. Create your feature branch (git checkout -b feat/my-feature)
  3. Write specs covering your changes in spec/
  4. Ensure crystal spec, crystal tool format --check src spec, and ameba pass
  5. Open a Pull Request

License

Distributed under the MIT License. See LICENSE for details.

Contact

Elias J. Perez — @eliasjpr · https://github.com/azutoolkit/authly

About

OAuth2 Provider Library - Authly is an OAuth2 Library for creating Authorization Servers that follows OAuth2 authorization mechanisms.

Topics

Resources

Stars

34 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages