{"id":32158354,"url":"https://github.com/thiago-simoes/orionauth.jl","last_synced_at":"2026-02-18T22:01:46.441Z","repository":{"id":291272964,"uuid":"977095998","full_name":"Thiago-Simoes/OrionAuth.jl","owner":"Thiago-Simoes","description":"OrionAuth is a lightweight web authentication package written in Julia, designed for secure, scalable applications.","archived":false,"fork":false,"pushed_at":"2025-10-19T20:48:36.000Z","size":486,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-19T22:17:06.485Z","etag":null,"topics":["api","authentication","web-development"],"latest_commit_sha":null,"homepage":"https://thiago-simoes.github.io/OrionAuth.jl/build","language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Thiago-Simoes.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-03T12:16:28.000Z","updated_at":"2025-10-19T20:48:40.000Z","dependencies_parsed_at":"2025-05-03T15:24:54.861Z","dependency_job_id":"8ba9c196-f658-4535-90c8-0e6e88e9c32d","html_url":"https://github.com/Thiago-Simoes/OrionAuth.jl","commit_stats":null,"previous_names":["thiago-simoes/nebulaauth.jl","thiago-simoes/orionauth.jl"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Thiago-Simoes/OrionAuth.jl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thiago-Simoes%2FOrionAuth.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thiago-Simoes%2FOrionAuth.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thiago-Simoes%2FOrionAuth.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thiago-Simoes%2FOrionAuth.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Thiago-Simoes","download_url":"https://codeload.github.com/Thiago-Simoes/OrionAuth.jl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thiago-Simoes%2FOrionAuth.jl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29596329,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-18T20:59:56.587Z","status":"ssl_error","status_checked_at":"2026-02-18T20:58:41.434Z","response_time":162,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","authentication","web-development"],"created_at":"2025-10-21T13:00:20.920Z","updated_at":"2026-02-18T22:01:46.435Z","avatar_url":"https://github.com/Thiago-Simoes.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OrionAuth.jl\n\n## Objective\n\nThis project aims to provide a modern, complete, and robust Web Authentication package for Julia that is **framework-agnostic**.\n\nOrionAuth.jl now supports multiple web frameworks including:\n- **Genie.jl** - Full-featured MVC web framework\n- **Oxygen.jl** - Lightweight web framework  \n- **HTTP.jl** - Direct HTTP server usage\n- **Generic** - Any framework with custom request context\n\n**Security note:** While production-ready features are being added, please review security considerations for your use case.\n\n## Features\n\n- 🔐 **JWT-based Authentication** with HS256/HS512 algorithms\n- 👥 **Role-Based Access Control (RBAC)** with permissions\n- 🔑 **Multiple Password Hashing Algorithms** (Argon2id, SHA-512 legacy)\n- 🔄 **Password Reset Flow** with secure token generation and expiration\n- 🌐 **Framework-Agnostic Design** - works with Genie, Oxygen, HTTP.jl and more\n- 📝 **Comprehensive API** with docstrings and type safety\n- ✅ **Well-Tested** with extensive test coverage\n\n## Installation\n\nThe package can be installed with the Julia package manager.\nFrom the Julia REPL, type `]` to enter the Pkg REPL mode and run:\n\n```\npkg\u003e add \"https://github.com/Thiago-Simoes/OrionAuth.jl\"\n```\n\nOr, equivalently, via the `Pkg` API:\n\n```julia\njulia\u003e import Pkg; Pkg.add(\"https://github.com/Thiago-Simoes/OrionAuth.jl\")\n```\n\n## Quick Start\n\n### Configuration\n\nOrionAuth can be configured to work with your framework of choice:\n\n**Option 1: Environment Variable (Recommended)**\n```env\n# In your .env file\nORIONAUTH_FRAMEWORK=genie  # or: oxygen, http, auto\n```\n\n**Option 2: Explicit Configuration**\n```julia\nusing OrionAuth\nconfigure_framework!(:genie)  # :oxygen, :http, or :auto\n```\n\n**Option 3: Auto-Detection (Default)**\nOrionAuth automatically detects your framework when you use it.\n\n### With Genie.jl\n\n```julia\nusing Genie, Genie.Router\nusing OrionAuth\n\n# Initialize OrionAuth\nOrionAuth.init!()\n\n# Load Genie adapter\nBase.include(OrionAuth, joinpath(dirname(pathof(OrionAuth)), \"adapters/genie.jl\"))\n\n# Public route - signup/signin\nroute(\"/api/auth/signup\") do\n    payload = jsonpayload()\n    user, jwt_data = signup(payload[\"email\"], payload[\"name\"], payload[\"password\"])\n    return jwt_data\nend\n\nroute(\"/api/auth/signin\") do\n    payload = jsonpayload()\n    user, jwt_data = signin(payload[\"email\"], payload[\"password\"])\n    return jwt_data\nend\n\n# Protected route - auto-detects context, auto-handles errors!\nroute(\"/api/protected\") do\n    payload = Auth()  # That's it! No manual context or error handling\n    return \"Welcome, \\$(payload[\"name\"])!\"\nend\n\n# Permission-protected route\nroute(\"/api/admin\") do\n    payload = Auth(\"admin\")  # Automatically throws Genie exception if unauthorized\n    return \"Admin access granted\"\nend\n\nGenie.up()\n```\n\n### With HTTP.jl\n\n```julia\nusing HTTP\nusing JSON3\nusing OrionAuth\n\n# Initialize OrionAuth\nOrionAuth.init!()\n\n# Configure framework (or set ORIONAUTH_FRAMEWORK=http in .env)\nconfigure_framework!(:http)\n\n# Create HTTP server\nHTTP.serve(\"127.0.0.1\", 8080) do req\n    # Signup endpoint\n    if req.target == \"/signup\" \u0026\u0026 req.method == \"POST\"\n        data = JSON3.read(String(req.body))\n        user, jwt_data = signup(data[\"email\"], data[\"name\"], data[\"password\"])\n        return HTTP.Response(200, jwt_data)\n    end\n    \n    # Signin endpoint\n    if req.target == \"/signin\" \u0026\u0026 req.method == \"POST\"\n        data = JSON3.read(String(req.body))\n        user, jwt_data = signin(data[\"email\"], data[\"password\"])\n        return HTTP.Response(200, jwt_data)\n    end\n    \n    # Protected endpoint - simplified!\n    if req.target == \"/protected\"\n        payload = Auth(request=req)  # Auto-converts errors to HTTP.Response!\n        response_data = JSON3.write(Dict(\"message\" =\u003e \"Welcome, \\$(payload[\"name\"])!\"))\n        return HTTP.Response(200, response_data)\n    end\n    \n    return HTTP.Response(404, \"Not Found\")\nend\n```\n\n### With Oxygen.jl\n\n```julia\nusing Oxygen\nusing JSON3\nusing OrionAuth\n\n# Initialize OrionAuth\nOrionAuth.init!()\n\n# Configure framework (or set ORIONAUTH_FRAMEWORK=oxygen in .env)\nconfigure_framework!(:oxygen)\n\n# Signup route\n@post \"/signup\" function(req)\n    data = JSON3.read(String(req.body))\n    user, jwt_data = signup(data[\"email\"], data[\"name\"], data[\"password\"])\n    return json(jwt_data)\nend\n\n# Signin route\n@post \"/signin\" function(req)\n    data = JSON3.read(String(req.body))\n    user, jwt_data = signin(data[\"email\"], data[\"password\"])\n    return json(jwt_data)\nend\n\n# Protected route - simplified!\n@get \"/protected\" function(req)\n    payload = Auth(request=req)  # Auto-converts errors to HTTP.Response!\n    return json(Dict(\"message\" =\u003e \"Welcome, \\$(payload[\"name\"])!\"))\nend\n\nserve()\n```\n\n## Core Concepts\n\n### Simplified API (Recommended)\n\nOrionAuth now provides a simplified API that eliminates repetition:\n\n```julia\n# Genie - no context creation needed!\nroute(\"/api/users\") do\n    payload = Auth(\"admin\")  # Auto-detects, auto-handles errors\n    # ... your code\nend\n\n# HTTP.jl / Oxygen - just pass the request\nHTTP.serve() do req\n    payload = Auth(\"admin\", request=req)  # Auto-converts errors\n    # ... your code\nend\n```\n\n**Benefits:**\n- ✅ No manual context creation in every route (DRY!)\n- ✅ Automatic error conversion to framework-specific format\n- ✅ Configure once, use everywhere\n- ✅ Backward compatible with explicit context API\n\n### Request Context (Advanced)\n\nFor advanced use cases, you can still use explicit contexts:\n\n```julia\n# Generic context (for testing or custom frameworks)\nctx = GenericRequestContext(Dict(\"Authorization\" =\u003e \"Bearer \\$token\"))\n\n# Genie context\nctx = GenieRequestContext()  # Uses current Genie request\n\n# HTTP.jl context\nctx = HTTPRequestContext(req)\n\n# Oxygen context\nctx = OxygenRequestContext(req)\n\n# Use with explicit context\npayload = Auth(ctx, \"admin\")\n```\n\n### Authentication\n\n**Simplified (Recommended):**\n```julia\n# Genie\npayload = Auth()                    # Basic auth\npayload = Auth(\"admin\")             # With permission\npayload = Auth([\"read\", \"write\"])   # Multiple permissions\n\n# HTTP.jl / Oxygen\npayload = Auth(request=req)\npayload = Auth(\"admin\", request=req)\n```\n\n**Explicit Context (Advanced):**\n```julia\nctx = GenieRequestContext()\npayload = Auth(ctx)\npayload = Auth(ctx, \"admin\")\npayload = Auth(ctx, [\"read\", \"write\"])\n```\n\n### Roles and Permissions\n\n```julia\n# Create roles and permissions\nsyncRolesAndPermissions(Dict(\n    \"admin\" =\u003e [\"read\", \"write\", \"delete\"],\n    \"user\" =\u003e [\"read\"],\n    \"moderator\" =\u003e [\"read\", \"write\"]\n))\n\n# Assign role to user\nassignRole(user_id, \"admin\")\n\n# Assign direct permission\nassignPermission(user_id, \"special_action\")\n\n# Check user permissions\npermissions = getUserPermissions(user_id)\nhas_admin = checkPermission(user_id, \"admin\")\n\n# Remove role\nremoveRole(user_id, \"moderator\")\n```\n\n### Password Hashing\n\nOrionAuth supports multiple password hashing algorithms:\n\n```julia\n# Argon2id (default, recommended)\nhashed = hash_password(\"my_password\")\n\n# Legacy SHA-512 (for backward compatibility)\nhashed = hash_password(\"my_password\", algorithm=:sha512)\n\n# Verify password (auto-detects algorithm)\nis_valid = verify_password(stored_hash, \"my_password\")\n```\n\nConfigure the default algorithm via environment variable:\n```bash\nOrionAuth_PASSWORD_ALGORITHM=argon2id  # or sha512\n```\n\n### Password Reset\n\nOrionAuth provides a complete password reset flow with token generation, validation, and email integration:\n\n```julia\n# 1. Request password reset (without email)\ntoken = request_password_reset(\"user@example.com\")\nprintln(\"Reset token: $token\")\n\n# 2. Request password reset with email function\nfunction send_mail(recipient::String, subject::String, body::String)\n    # Integrate with your email service (e.g., SendGrid, AWS SES, SMTP)\n    # The body is HTML formatted\n    println(\"Sending email to: $recipient\")\n    println(\"Subject: $subject\")\n    # Your email sending logic here\nend\n\ntoken = request_password_reset(\"user@example.com\", send_mail=send_mail)\n\n# 3. Verify token validity\ntoken_info = verify_reset_token(token)\nif !isnothing(token_info)\n    println(\"Token is valid for user ID: $(token_info.userId)\")\nelse\n    println(\"Token is invalid or expired\")\nend\n\n# 4. Reset password with token\nsuccess = reset_password_with_token(token, \"newSecurePassword123\")\nif success\n    println(\"Password reset successful!\")\nelse\n    println(\"Invalid or expired token\")\nend\n```\n\n**Configuration:**\n```bash\n# Set token expiration time in minutes (default: 60)\nOrionAuth_PASSWORD_RESET_TOKEN_EXPIRATION=60\n```\n\n**Complete Example with Genie.jl:**\n```julia\nusing Genie, Genie.Router\nusing OrionAuth\nusing JSON3\n\nOrionAuth.init!()\nBase.include(OrionAuth, joinpath(dirname(pathof(OrionAuth)), \"adapters/genie.jl\"))\n\n# Email sending function (example with SMTP)\nfunction send_reset_email(recipient, subject, body)\n    # Example: Use SMTPClient.jl or your preferred email service\n    # SMTPClient.send(\n    #     from=\"noreply@yourapp.com\",\n    #     to=recipient,\n    #     subject=subject,\n    #     body=body\n    # )\n    println(\"Email sent to: $recipient\")\nend\n\n# Request password reset endpoint\nroute(\"/api/auth/request-password-reset\", method=POST) do\n    payload = jsonpayload()\n    email = payload[\"email\"]\n    \n    try\n        # Token is sent via email, not returned in response\n        request_password_reset(email, send_mail=send_reset_email)\n        return json(Dict(\"message\" =\u003e \"If the email exists, a reset link has been sent\"))\n    catch e\n        if occursin(\"User not found\", string(e))\n            # Return same message for security (don't reveal if email exists)\n            return json(Dict(\"message\" =\u003e \"If the email exists, a reset link has been sent\"))\n        end\n        throw(Genie.Exceptions.ExceptionalResponse(500, [], \"Internal server error\"))\n    end\nend\n\n# Reset password endpoint\nroute(\"/api/auth/reset-password\", method=POST) do\n    payload = jsonpayload()\n    token = payload[\"token\"]\n    new_password = payload[\"new_password\"]\n    \n    success = reset_password_with_token(token, new_password)\n    \n    if success\n        return json(Dict(\"message\" =\u003e \"Password reset successful\"))\n    else\n        throw(Genie.Exceptions.ExceptionalResponse(400, [], \"Invalid or expired token\"))\n    end\nend\n\nGenie.up()\n```\n\n**Complete Example with HTTP.jl:**\n```julia\nusing HTTP\nusing JSON3\nusing OrionAuth\n\nOrionAuth.init!()\nconfigure_framework!(:http)\n\nfunction send_reset_email(recipient, subject, body)\n    println(\"Email sent to: $recipient\")\n    # Your email integration here\nend\n\nHTTP.serve(\"127.0.0.1\", 8080) do req\n    # Request password reset\n    if req.target == \"/request-password-reset\" \u0026\u0026 req.method == \"POST\"\n        data = JSON3.read(String(req.body))\n        \n        try\n            request_password_reset(data[\"email\"], send_mail=send_reset_email)\n            response = JSON3.write(Dict(\"message\" =\u003e \"If the email exists, a reset link has been sent\"))\n            return HTTP.Response(200, response)\n        catch e\n            response = JSON3.write(Dict(\"error\" =\u003e \"Request failed\"))\n            return HTTP.Response(500, response)\n        end\n    end\n    \n    # Reset password\n    if req.target == \"/reset-password\" \u0026\u0026 req.method == \"POST\"\n        data = JSON3.read(String(req.body))\n        \n        success = reset_password_with_token(data[\"token\"], data[\"new_password\"])\n        \n        if success\n            response = JSON3.write(Dict(\"message\" =\u003e \"Password reset successful\"))\n            return HTTP.Response(200, response)\n        else\n            response = JSON3.write(Dict(\"error\" =\u003e \"Invalid or expired token\"))\n            return HTTP.Response(400, response)\n        end\n    end\n    \n    return HTTP.Response(404, \"Not Found\")\nend\n```\n\n**Email Function Interface:**\n\nThe `send_mail` function must accept three string arguments:\n- `recipient::String`: Email address to send to\n- `subject::String`: Email subject line\n- `body::String`: Email body in HTML format\n\nExample integrations:\n```julia\n# Example 1: Console logging (for development/testing)\nfunction dev_send_mail(recipient, subject, body)\n    println(\"=\" ^ 80)\n    println(\"TO: $recipient\")\n    println(\"SUBJECT: $subject\")\n    println(\"BODY:\\n$body\")\n    println(\"=\" ^ 80)\nend\n\n# Example 2: SMTP integration (pseudo-code)\nusing SMTPClient\nfunction smtp_send_mail(recipient, subject, body)\n    send(\n        server=\"smtp.gmail.com\",\n        port=587,\n        username=ENV[\"SMTP_USERNAME\"],\n        password=ENV[\"SMTP_PASSWORD\"],\n        from=\"noreply@yourapp.com\",\n        to=recipient,\n        subject=subject,\n        message=body,\n        ishtml=true\n    )\nend\n\n# Example 3: SendGrid API (pseudo-code)\nusing HTTP, JSON3\nfunction sendgrid_send_mail(recipient, subject, body)\n    url = \"https://api.sendgrid.com/v3/mail/send\"\n    headers = [\"Authorization\" =\u003e \"Bearer $(ENV[\"SENDGRID_API_KEY\"])\",\n               \"Content-Type\" =\u003e \"application/json\"]\n    \n    payload = Dict(\n        \"personalizations\" =\u003e [Dict(\"to\" =\u003e [Dict(\"email\" =\u003e recipient)])],\n        \"from\" =\u003e Dict(\"email\" =\u003e \"noreply@yourapp.com\"),\n        \"subject\" =\u003e subject,\n        \"content\" =\u003e [Dict(\"type\" =\u003e \"text/html\", \"value\" =\u003e body)]\n    )\n    \n    HTTP.post(url, headers, JSON3.write(payload))\nend\n```\n\n## Migration Guide\n\n### For Existing Genie Users\n\nYour existing code continues to work! OrionAuth now offers even simpler APIs:\n\n**Original (still works):**\n```julia\nroute(\"/protected\") do\n    Auth()  # Works exactly as before\n    return \"Protected\"\nend\n```\n\n**Even Simpler (new):**\nNo changes needed! The simplified API is already what you're using. But now you can configure the framework once and it auto-handles errors:\n\n```julia\n# Configure once at startup (optional - auto-detects Genie)\nconfigure_framework!(:genie)\n\n# Or in .env\nORIONAUTH_FRAMEWORK=genie\n\n# Then in all routes - same simple code, but errors auto-convert!\nroute(\"/protected\") do\n    Auth()  # Auto-detects context, auto-converts errors\n    return \"Protected\"\nend\n```\n\n**Manual Context (advanced, if needed):**\n```julia\nroute(\"/protected\") do\n    ctx = GenieRequestContext()\n    Auth(ctx)  # Explicit context\n    return \"Protected\"\nend\n```\n\n### Comparison: Before vs After\n\n| **Before** | **After (Simplified)** |\n|------------|------------------------|\n| `ctx = GenieRequestContext(); try { Auth(ctx) } catch...` | `Auth()` |\n| Manual error handling in every route | Automatic error conversion |\n| Repeat context creation everywhere | Configure once, use everywhere |\n\n## Configuration\n\nOrionAuth uses environment variables for configuration. Create a `.env` file:\n\n```env\n# Framework Configuration (Optional - auto-detects if not set)\nORIONAUTH_FRAMEWORK=genie  # Options: genie, oxygen, http, auto\n\n# Database Configuration\nDB_HOST=localhost\nDB_USER=root\nDB_PASSWORD=your_password\nDB_NAME=your_database\nDB_PORT=3306\n\n# JWT Configuration\nOrionAuth_SECRET=your-secret-key-here\nOrionAuth_ALGORITHM=HS256  # or HS512\nOrionAuth_JWT_EXP=30  # expiration in minutes\n\n# Password Configuration\nOrionAuth_PASSWORD_ALGORITHM=argon2id  # or sha512\nOrionAuth_MIN_PASSWORD_ITTERATIONS=25000  # for SHA-512 legacy\n\n# Password Reset Configuration\nOrionAuth_PASSWORD_RESET_TOKEN_EXPIRATION=60  # token expiration in minutes (default: 60)\n```\n\n## Documentation\n\n- [**STABLE**](https://thiago-simoes.github.io/ORM.jl/) \u0026mdash; **documentation of the most recently tagged version.**\n\n## Project Status\n\nThe package is tested against, and being developed for, Julia `1.6` and above on Linux, macOS, and Windows.\n\n## Collaboration\n\nContributions are welcome!  \nFeel free to open pull requests or issues with suggestions and enhancements.\n\n## Vulnerabilities\n\nIf you discover any **vulnerabilities**, please report them via issues.\n\n### How to build docs?\n$ julia --project make.jl\n\n## License\nOrionAuth is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthiago-simoes%2Forionauth.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthiago-simoes%2Forionauth.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthiago-simoes%2Forionauth.jl/lists"}