GithubHelp home page GithubHelp logo

codemation / easyauth Goto Github PK

View Code? Open in Web Editor NEW
526.0 526.0 46.0 3.7 MB

Create a centralized Authentication and Authorization token server. Easily secure FastAPI endpoints based on Users, Groups, Roles or Permissions with very little database usage.

Home Page: https://easyauth.readthedocs.io/en/latest/

License: MIT License

Python 99.46% Dockerfile 0.39% Shell 0.15%
admin-dashboard authentication authorization fastapi gui jwt permissions rbac user-management

easyauth's People

Contributors

akyl0221 avatar codemation avatar foreztgump avatar rictus avatar yezz123 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

easyauth's Issues

docker entrypoint file

hi, thanks for this project. I want to run this project in docker , but I get an error.

docker run --name easyauth \
    -e DB_TYPE=sqlite \
    -e DB_NAME=auth \
    -e DB_LOCAL_PATH=/mnt/easyauth \
    -e ISSUER=EasyAuth \
    -e SUBJECT=EasyAuthAuth \
    -e AUDIENCE=EasyAuthApis \
    -e KEY_PATH=/mnt/easyauth \
    -e KEY_NAME=test_key \
    -v $(pwd)/easyauth-vol:/mnt/easyauth \
    -p 8220:8220 \
    -d joshjamison/easyauth:v0.0.0

./entrypoint.sh: line 3: gunicorn: command not found

EasyAuthServer with mysql backend setup failing

Summary:

EasyAuthServer with mysql backend setup failing

Steps to reproduce:

1 - Create MYSQL database:

# env="-e MYSQL_ROOT_PASSWORD=abcd1234 -e MYSQL_USER=josh -e MYSQL_PASSWORD=abcd1234 -e MYSQL_DATABASE=joshdb"

#  start mysql database
# mkdir -p mysql-vol
# docker run --name mysql -v $(pwd)/mysql-vol:/var/lib/mysql $env -p 3306:3306 -d mysql

# configure EasyAuthServer to use mysql
# cat > auth_server_config.json << EOF
{
    "DB_TYPE": "mysql",
    "DB_NAME": "auth",
    "DB_HOST": "0.0.0.0",
    "DB_PORT": "3306",
    "DB_USER": "codemation",
    "DB_PASSWORD": "abcd1234",
    "ISSUER": "EasyAuth",
    "SUBJECT": "EasyAuth",
    "AUDIENCE": "EasyAuthApis",
    "KEY_PATH": "/home/codemation/python/project/tests",
    "KEY_NAME": "test_key"
}
EOF 
  1. Start EasyAuthServer
from fastapi import FastAPI

from easyauth.server import EasyAuthServer

server = FastAPI()

@server.on_event('startup')
async def startup():
    server.auth = await EasyAuthServer.create(
        server, 
        '/auth/token',
        env_from_file='auth_server_config.json'
    )

Errors / Traceback

04-20 12:10 aiopyql-db-auth DEBUG    auth - execute: CREATE TABLE users (username VARCHAR(36) PRIMARY KEY UNIQUE NOT NULL, full_name TEXT , email TEXT , account_type TEXT , password TEXT , groups TEXT )
04-20 12:10 aiopyql-db-auth DEBUG    completed query: aabaa7a9-a1c0-11eb-ac04-ed4c20b9a885
04-20 12:10 aiopyql-db-auth ERROR    error during create_table - ProgrammingError(1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'groups TEXT )' at line 1")

Cannot start example server on Windows

When I try to start a basic server as described on the easyauth github page, I run into an error telling me this, i.e. "fcntl" cant be imported as it is only available on Linux.

Traceback (most recent call last): File "C:\Users\DELL\AppData\Local\Programs\Python\Python37\lib\runpy.py", line 193, in _run_module_as_main "__main__", mod_spec) File "C:\Users\DELL\AppData\Local\Programs\Python\Python37\lib\runpy.py", line 85, in _run_code exec(code, run_globals) File "C:\Users\DELL\AppData\Local\Programs\Python\Python37\Scripts\gunicorn.exe\__main__.py", line 4, in <module> File "C:\Users\DELL\AppData\Local\Programs\Python\Python37\lib\site-packages\gunicorn\app\wsgiapp.py", line 9, in <module> from gunicorn.app.base import Application File "C:\Users\DELL\AppData\Local\Programs\Python\Python37\lib\site-packages\gunicorn\app\base.py", line 11, in <module> from gunicorn import util File "C:\Users\DELL\AppData\Local\Programs\Python\Python37\lib\site-packages\gunicorn\util.py", line 8, in <module> import fcntl

Any suggestion about how to fix this would help me a lot, I saw in the issues some people used this on windows before so I hope there might be a solution. Thanks!

Protected routes by easyauth are not included in swagger docs

When protecting routes with Easyauth, swagger /docs do not include protected endpoints created in the controller that configured the route.

app factory

project_auth_router = app.auth.create_api_router(
            prefix=settings.api_prefix+"/projects"
        )

        from app.controllers import project_controller
        await project_controller.setup(project_auth_router)

project_controller

from fastapi import Depends
from typing import List
from sqlalchemy.orm import Session
from app.schemas.project_schemas import ProjectSchema, ProjectCreateSchema
from app.usecases.project_service import ProjectService
from app.middlewares import deps, di


async def setup(router):
    @router.get("/",
        users=['admin'],
        groups=['users', 'admins'],
        response_model=List[ProjectSchema],
        include_in_schema=False
    )
    def read_items(
        commons: dict = Depends(di.common_parameters),
        db: Session = Depends(deps.get_db)
    ):
        projects = ProjectService.reads(
            db,
            skip=commons['skip'],
            limit=commons['limit']
        )
        return projects

    @router.post("/",
        users=['admin'],
        groups=['users', 'admins', 'administrators'],
        include_in_schema=False,
        response_model=ProjectSchema
    )
    def create_project(
        item: ProjectCreateSchema,
        db: Session = Depends(deps.get_db)
    ):
        return ProjectService.create(db=db, item=item)

Routes do work and are protected, however, no swaggers docs are exposed.

Any thoughts / workarounds?

Can't load plugin: sqlalchemy.dialects:postgres

How can i override this issue?

File "C:\Users\PC\codmeric\test_prject\venv\lib\site-packages\sqlalchemy\util\langhelpers.py", line 343, in load
raise exc.NoSuchModuleError(
sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres

[Question] cross-provider support

Hi @codemation,

Hope you find very well.

This tool looks fantastic. I was wondering if there will be plans in the roadmap to make this tool cross-provider, e.g. to work with microsoft login as well.

401 Unauthorized - When loging in for the first time on admin

I installed the easy-auth with pip install easy-auth[server] has a missing dependancy 'aiosqlite'. When I run the main file I and head onto /login page it throws the error as below

Error Log

  • INFO: Application startup complete.
  • INFO: 127.0.0.1:56843 - "GET /login HTTP/1.1" 200 OK
  • INFO: 127.0.0.1:56844 - "POST /login HTTP/1.1" 302 Found
  • 07-26 15:37 EasyRpc-server /ws/manager WARNING global_store_update - update - tokens - 9a71e09f-da83-406d-9d29-c55473b056ee
  • INFO: 127.0.0.1:56844 - "GET /admin/ HTTP/1.1" 401 Unauthorized
  • INFO: 127.0.0.1:56870 - "GET / HTTP/1.1" 200 OK
  • INFO: 127.0.0.1:56870 - "GET /admin/ HTTP/1.1" 401 Unauthorized
  • INFO: 127.0.0.1:56899 - "POST /login HTTP/1.1" 302 Found
  • 07-26 15:38 EasyRpc-server /ws/manager WARNING global_store_update - update - tokens - 7c984e1c-1416-430d-acc0-aae1095dcb00
  • INFO: 127.0.0.1:56899 - "GET /admin/ HTTP/1.1" 401 Unauthorized

Pip Freeze Output
aiohttp==3.8.5
aiosignal==1.3.1
aiosmtplib==2.0.2
aiosqlite==0.19.0
alembic==1.8.1
annotated-types==0.5.0
anyio==3.7.1
async-timeout==4.0.2
attrs==23.1.0
bcrypt==4.0.1
blinker==1.6.2
cachetools==5.3.1
certifi==2023.7.22
cffi==1.15.1
charset-normalizer==3.2.0
click==8.1.6
cryptography==41.0.2
databases==0.5.3
Deprecated==1.2.14
dnspython==2.4.0
easy-auth==2.0.1
easyadmin==0.170
easyrpc==0.245
easyschedule==0.107
email-validator==1.1.3
example==0.1.0
exceptiongroup==1.1.2
fastapi==0.100.0
fastapi-mail==1.3.1
frozenlist==1.4.0
google-api-core==2.11.1
google-api-python-client==2.31.0
google-auth==2.22.0
google-auth-httplib2==0.1.0
googleapis-common-protos==1.59.1
gunicorn==20.1.0
h11==0.14.0
httpcore==0.17.3
httplib2==0.22.0
httptools==0.3.0
httpx==0.24.1
idna==3.4
itsdangerous==2.1.2
Jinja2==3.1.2
jwcrypto==1.5.0
makefun==1.9.5
Mako==1.2.4
MarkupSafe==2.1.3
multidict==6.0.4
orjson==3.9.2
protobuf==4.23.4
pyasn1==0.5.0
pyasn1-modules==0.3.0
pycparser==2.21
pydantic==1.10.12
pydantic-extra-types==2.0.0
pydantic-settings==2.0.2
pydantic_core==2.4.0
pydbantic==0.0.41
PyJWT==2.0.0
pyparsing==3.1.0
python-dotenv==1.0.0
python-jwt==4.0.0
python-multipart==0.0.6
PyYAML==6.0.1
redis==4.6.0
requests==2.31.0
rsa==4.9
six==1.16.0
sniffio==1.3.0
SQLAlchemy==1.4.28
starlette==0.27.0
typing_extensions==4.7.1
ujson==5.8.0
uritemplate==4.1.1
urllib3==1.26.16
uvicorn==0.23.1
uvloop==0.17.0
watchfiles==0.19.0
websockets==11.0.3
wrapt==1.15.0
yarl==1.9.2

Password:
07-26 15:36 EasyAuthServer ERROR detected new EasyAuth server, created admin user with password: ropdqzfz

Other OAuth Provider

Hi,
is it easy or planned to add other oauth provider like Github?

Thanks,
bert

PyJWT out of date in setup.py

Took me a minute to find, but PyJWT is out of date in setup.py causing a hang in pip when installing all requirements if PyJWT is specified in requirements.txt as 2.1.0.

Error on loading a app

I have attempted to install easy auth in an existing fast API project. There are the following 2 issues encountered:

  1. Pip/Poetry add do not install all dependencies. The following dependencies are getting missed and need to be installed
    using pip or poetry one by one.
  • pydbantic
  • bycrypt
  • fastapi-mail
  • gunicorn
  • eamil-validator
  • google-api-python-client
  1. Secondly, I am getting the below error on starting the server. Not sure what is causing this problem

`ERROR: Traceback (most recent call last):
File "/Users/ahmad/Library/CloudStorage/OneDrive-Personal/Coding/FastApi/blog-app/.venv/lib/python3.10/site-packages/starlette/routing.py", line 671, in lifespan
async with self.lifespan_context(app):
File "/Users/ahmad/Library/CloudStorage/OneDrive-Personal/Coding/FastApi/blog-app/.venv/lib/python3.10/site-packages/starlette/routing.py", line 566, in aenter
await self._router.startup()
File "/Users/ahmad/Library/CloudStorage/OneDrive-Personal/Coding/FastApi/blog-app/.venv/lib/python3.10/site-packages/starlette/routing.py", line 648, in startup
await handler()
File "/Users/ahmad/Library/CloudStorage/OneDrive-Personal/Coding/FastApi/blog-app/./blog/main.py", line 26, in startup
app.auth = await EasyAuthServer.create(
File "/Users/ahmad/Library/CloudStorage/OneDrive-Personal/Coding/FastApi/blog-app/.venv/lib/python3.10/site-packages/easyauth/server.py", line 225, in create
auth_server = cls(
File "/Users/ahmad/Library/CloudStorage/OneDrive-Personal/Coding/FastApi/blog-app/.venv/lib/python3.10/site-packages/easyauth/server.py", line 107, in init
self.load_env_from_file(env_from_file)
File "/Users/ahmad/Library/CloudStorage/OneDrive-Personal/Coding/FastApi/blog-app/.venv/lib/python3.10/site-packages/easyauth/server.py", line 362, in load_env_from_file
env_file = json.load(json_env)
File "/Users/ahmad/.pyenv/versions/3.10.9/lib/python3.10/json/init.py", line 293, in load
return loads(fp.read(),
File "/Users/ahmad/.pyenv/versions/3.10.9/lib/python3.10/json/init.py", line 346, in loads
return _default_decoder.decode(s)
File "/Users/ahmad/.pyenv/versions/3.10.9/lib/python3.10/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/Users/ahmad/.pyenv/versions/3.10.9/lib/python3.10/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

ERROR: Application startup failed. Exiting.`

Envoirnemnt specification
python: 3.10.9
OS: MacOS

Action not being deleted from "Roles_to_Actions" table in database

While testing my EasyAuth Client, I came across an odd behavior. Basically, when I delete an action via web GUI, the database does not get correctly updated, allowing for methods that depend on this action for permission to continue working. Here are the steps I followed inside the GUI:

  1. Create an action, let's say "TEST_ACTION";
  2. Create a role "test_role" and assign "TEST_ACTION" to it;
  3. Create a group "test_group" and assign "test_role" to it;
  4. Create a service "test_service" and assign "test_group" to it;
  5. Finally, generate a token for "test_service".

In my Client code, I defined a generic POST method to test if the token worked correctly, defining "TEST_ACTION" as the required permission. Then, I made a request to this method, sending the token as header, and everything worked perfectly.

After that, my next test was to force an "Unauthorized" error on this request by deleting "TEST_ACTION", so the token wouldn't have permission, and this is where I noticed the strange behavior: the request still worked. Have in mind that I only used the GUI to create and delete "TEST_ACTION", so I'm not sure if this behavior would repeat itself if I used only the default HTTP requests that are created when you instantiate the EasyAuth Server.

I checked the database file to see what was going on, and this is what I gathered: when I delete the action, it gets correctly removed from "Actions" table. However, this does not happen on "Roles_to_Actions" table, as the line "test_role | TEST_ACTION" continues there. So, when the program accesses this table to check if "test_role" has the necessary permissions, it allows the method to work. I tried restarting both Server and Client, but the results were the same.

Feature LDAP authentication

Have you ever consider integrating with LDAP?

I am thinking to use this in an internal project but I would like to have LDAP authentication.
It could the same method as storing the users in DB but authenticating against an LDAP server.

Also there could be a mapping between DB groups and LDAP groups in order to give permissions directly by assigning users to LDAP groups.

Import config api doesn't work

Hello everyone,

I have an issue with the export/import config api. When I export and import the default config I have an exception at the import.

File \"/usr/local/lib/python3.9/dist-packages/easyauth/api.py\", line 110, in import_auth_config\n for action in role[\"permissions\"][\"actions\"]:\nKeyError: 'permissions'"}
In my exported config my roles looks like:

"roles": [
    {
      "role": "admin",
      "actions": [
        {
          "action": "CREATE_USER",
          "details": "default action for creating users"
        }
      ]
    }
  ]

It's true I don't have the permissions key.

Python tool without service account

Hello,

I want to know how can I have a python tools which give me access to my authenticated api without creating a service account.

Actually, what I'm trying to do:

req_header = {
        "Content-Type": "application/json"
    }

    req_body = {
            "username": "myuser",
            "password": "xxxx"
    }

resp = requests.post(
        "http://127.0.0.1:5858/auth/token",
        headers=req_header,
        json=req_body,
        verify=False
    )

The goal below is to get a token to access to my other api but it doesn't works:

{'detail': [{'loc': ['body', 'username'], 'msg': 'field required', 'type': 'value_error.missing'}, {'loc': ['body', 'password'], 'msg': 'field required', 'type': 'value_error.missing'}]}

What Am it doing wrong?

Thanks for your help

Rémy

Extend User model

Hi there! Thanks for the awesome work here!

I would like to know what is the best approach to extend the user's model. Should I send a PR for those changes or is there any other way to extend that model from my app?.

Feature Request: Create user via API without authentication

Creating user manually or allowing users to create their own account via the /admin page works perfect. Let's say we create a front end to our FastAPI application and need the ability to create a new user. If we call http://0.0.0.0:8330/auth/user to create a user from the frontend this requires authentication. Ideally, creating a new user wouldn't require authentication so that it can be done via another frontend.

Create services not working

I have error when create service account in admin GUI
Traceback (most recent call last): File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 372, in run_asgi result = await app(self.scope, self.receive, self.send) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__ return await self.app(scope, receive, send) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/fastapi/applications.py", line 261, in __call__ await super().__call__(scope, receive, send) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/applications.py", line 112, in __call__ await self.middleware_stack(scope, receive, send) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 181, in __call__ raise exc File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 159, in __call__ await self.app(scope, receive, _send) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/middleware/base.py", line 63, in __call__ response = await self.dispatch_func(request, call_next) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/easyauth/server.py", line 176, in handle_401_403 response = await call_next(request) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/middleware/base.py", line 44, in call_next raise app_exc File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/middleware/base.py", line 34, in coro await self.app(scope, request.receive, send_stream.send) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/middleware/base.py", line 63, in __call__ response = await self.dispatch_func(request, call_next) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/easyauth/server.py", line 172, in detect_token_in_cookie return await call_next(request) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/middleware/base.py", line 44, in call_next raise app_exc File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/middleware/base.py", line 34, in coro await self.app(scope, request.receive, send_stream.send) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/middleware/cors.py", line 92, in __call__ await self.simple_response(scope, receive, send, request_headers=headers) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/middleware/cors.py", line 147, in simple_response await self.app(scope, receive, send) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/exceptions.py", line 82, in __call__ raise exc File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/exceptions.py", line 71, in __call__ await self.app(scope, receive, sender) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__ raise e File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__ await self.app(scope, receive, send) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/routing.py", line 656, in __call__ await route.handle(scope, receive, send) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/routing.py", line 259, in handle await self.app(scope, receive, send) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/starlette/routing.py", line 61, in app response = await func(request) File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/fastapi/routing.py", line 227, in app raw_response = await run_endpoint_function( File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/fastapi/routing.py", line 160, in run_endpoint_function return await dependant.call(**values) File "<makefun-gen-6>", line 2, in create_service File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/easyauth/router.py", line 171, in mock_function return await result File "/Users/kel/maddevs/collector/easyauth/venv/lib/python3.10/site-packages/easyauth/api.py", line 467, in create_service user_groups = [await verify_group(group) for group in user.groups] NameError: name 'user' is not defined

In the function create_service need to change

from

user_groups = [await verify_group(group) for group in user.groups]

to

service_groups = [await verify_group(group) for group in service.groups]

Default value missing

Hello,

parse_permissions is called multiple times in the codebase but is always missing the default_permissions argument. I guess it miss a default value ?

def parse_permissions(self, users, groups, roles, actions, default_permissions):

My suggestion:

def parse_permissions(self, users, groups, roles, actions, default_permissions=None):

Feature - Implement Token Revoking mechanism for issued Tokens

Implement Token Revoking mechanism for issued Tokens

There are two ways that an issued token could be revoked.

  • Re-creating / Rotating the RSA Key
  • Tracking the token in a client side Database ( always checking existence for validity)

Globally recreating the RSA key invalidates all un-expired tokens as the signature has changed. This is not ideal as re-distributing / rotating public keys is harder to automate but possible if EasyAuthClients maintain an active connection to the EasyAuthServer, the server could push changes downstream.

Tracking & Updating a valid token is also difficult without introducing a implementing a locally cached "source-of-truth" for all workers, as performing a Database call against every request to check validity will reduce overall performance, and keeping this cache up-to-date would require a long-polling against the EasyAuthServer for changes.

Possible Solution

Maintain an active connection between EasyAuthClient & EasyAuthServer via EasyRPC protocol(duplexed websocket connection).

This could allow EasyAuthServer to push updates to all connected EasyAuthClient's when a token has been revoked, but also enables a channel that would allow easier key rotation and could completely eliminate the need to distribute / manage client side public keys.

  • Implement EasyRPC channel between EasyAuthServer & EasyAuthServer
  • Using EasyRPC channel - allow public key to be pulled by EasyRPC clients
  • Implement push mechanism using EasyRPC channel to invalidate issued tokens
  • Create Token Admin GUI panel for issued Tokens
  • Create mechanism for rotating keys via EasyRPC channels

Potential security issue

Hello 👋

I run a security community that finds and fixes vulnerabilities in OSS. A researcher (@notnci) has found a potential issue, which I would be eager to share with you.

Could you add a SECURITY.md file with an e-mail address for me to send further details to? GitHub recommends a security policy to ensure issues are responsibly disclosed, and it would help direct researchers in the future.

Looking forward to hearing from you 👍

(cc @huntr-helper)

Documentation/guides

Can you provide guides or docs on how to:

  • Run both server and client from same instance
  • Create/use/view/revoke service keys

Sending emails when password contains characters that need to be escaped

I am getting the error "fastapi_mail.errors.ConnectionErrors: Exception raised (535, '5.7.8 Username and Password not accepted."

The password I am using works within Azure Functions. Could it be that when you are reading/writing the password its not being enclosed in quotes or being escaped?

Feature - Add Email Functionality

Email functionality is important to allow users / admins the following functionality:

  • - Add email functionality
  • - Create Email Configuration Documentation
  • - Send Activation Emails for New Users - validating account information provided.
  • - Send Password Reset links / codes / instructions
  • - Enabling 2-Factor authentication
  • - Sending on-demand or scheduled emails to subscribers
  • - Notifying Admins / Users about triggered events / issues

easyauth httptools 0.3.0 not good for python 3.11

when use conda python 3.11 env, when use poetry install there is compile error for httptools 0.3.0.
Just like:
longintrepr.h: No such file or directory .

Is there any solution update httptools to 0.5.0?

how to use in production

awesome package but would love to know how to use in production and the difference in the client and server because the docs were dark on that...would appreciate...thanks

Autorize openAPI widget vs easyauth login page

  1. when using the login easyauth page, the user is well authenticated and calling the APIs directly works as expected but from the openapi docs the login status is not in sync

  2. when using the /docs openapi authorize widget, the user "seems" to be well authenticated but all the API calls fail

"/groups" not working

Hi! After the update, I managed to get my test server and client working, so I tried creating a test service to check if I could make the requests properly, but stumbled across the error "{"detail":"not authorized, permissions required: {'groups': ['users', 'admins']}"}" when trying the /groups endpoint. It's probably some mistake on my side, so I was wondering if anyone could help me out.

Here are the steps I followed:

  1. Start both test server and client
  2. Create a service named "test service"
  3. Assign "test service" to "administrators" group
  4. Generate a token for "test service"
  5. Grab the token generated and insert it on the request header: curl -X GET http://<my_ip_address>:<my_port>/groups -H 'Content-Type: application/json' -H 'Authorization: Bearer <test_service_token>'

Am I missing something?

raise RuntimeError("Cannot add middleware after an application has started")

Thanks in advance for this tool, it is something I was looking for some time age. I was trying to initialize EasyAuthClient on new fastapi lifespan like this

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    await EasyAuthClient.create(
        app,
        token_server='0.0.0.0',
        token_server_port=8090,
        auth_secret='abcd1234',
        default_permissions={'groups': ['users']}
    )
    print("startup fastapi")
    yield
    # shutdown

and I get this message error
image

Do you have any idea how to run EasyAuthClient in lifespan also there is a way to use client as depends?

Add Support for multiple EasyAuthServer workers

Problem

Creating a solution for Issue4 & issue 5 - it became inherently more clear how important fork-ability is for the EasyAuthServer.

Production deployments typically run behind a production grade Gateway interface (gunicorn or other ), which create process forks ( AKA workers ) to handle requests on a separate process, GIL, and thus memory.

EasyAuthServer makes use of caching (to reduce required DB requests ). This caching works under the assumption that the worker accessing the DB is the one and only process communicating directly to/from the database. Creating forks, would then complicate this ...

Solution:

Implement a separate caching solution(redis / memcached) or proxy requests through a single process using EasyRPC. EasyRPC has an aiopyql interface which proxies database methods, allowing all proxy connection to share it cache.

  • Implement EasyRPC Database Proxy for EasyAuthServer auth database
  • Define static DEFAULT local ports for Database Proxy - used by all EasyAuthServer workers on 127.0.01:DEFAULT_PORT
  • Create Documentation for new DEFAULT_PORT usage

Feature - Add Google Federated Authentication

Google Oauth

Google Federated Login provides users with password-less authentication of a users identity. This should be integrated into EasyAuth to allow for ease of configuration, new user registration, existing user login.

  • Integrate Google Federated Login API's
  • Allow for GUI configuration of Google OAuth
  • Enable Google Sign / Out Functions at login pages
  • Setup Default permissions for new users ( Google and Other)

Related

  • Improve Token invalidation - 403 should not remove token from cookie or invalidate token

References

https://developers.google.com/identity/sign-in/web/sign-in
https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/

Refactor quorum setup & determination to match easycaching implementation

Problem

current quorum setup & determination can result in startup failures if quorum db is not correctly cleaned up. Typically issues only occur in development, but transient issues on startup can result in quorum failing to established.

Fix

Fix was implemented in easycaching, with a much simpler file locking approach to determine quorum leader & setup.

  • Implement quorum setup using easycaching implementation.

postgresql loop

Following the Server instructions for postgres, I encounter the following endless loop.

Not sure where the 8091 comes into play?
Cannot connect to host 127.0.0.1:8091 ssl:False [Connection refused]

======

INFO: Uvicorn running on http://0.0.0.0:8330 (Press CTRL+C to quit)
INFO: Started reloader process [18384] using statreload
get_chart name: DefaultChartName
INFO: Started server process [18386]
INFO: Waiting for application startup.
07-04 12:21 EasyAuthServer WARNING loading env vars from easyauth.json

[2021-07-04 12:21:23 -0400] [18394] [INFO] Starting gunicorn 20.1.0
[2021-07-04 12:21:23 -0400] [18394] [INFO] Listening at: http://127.0.0.1:8091 (18394)
[2021-07-04 12:21:23 -0400] [18394] [INFO] Using worker: uvicorn.workers.UvicornWorker
[2021-07-04 12:21:23 -0400] [18396] [INFO] Booting worker with pid: 18396
[2021-07-04 12:21:24 -0400] [18396] [INFO] Started server process [18396]
[2021-07-04 12:21:24 -0400] [18396] [INFO] Waiting for application startup.
07-04 12:21 EasyRpc-server /ws/easyauth ERROR error running query:
create or replace function show_tables() returns SETOF text as $$
SELECT
table_name
FROM
information_schema.tables
WHERE
table_type = 'BASE TABLE'
AND
table_schema NOT IN ('pg_catalog', 'information_schema');
$$
language sql;

Traceback (most recent call last):
File "/home/python/venv/lib/python3.8/site-packages/aiopyql/data.py", line 393, in __process_queue
results = await self.process_query_no_commit(self, conn, query_id, query)
File "/home/python/venv/lib/python3.8/site-packages/aiopyql/postgres_connector.py", line 260, in process_query_no_commit
results = await conn.fetch(query)
File "/home/python/venv/lib/python3.8/site-packages/asyncpg/connection.py", line 583, in fetch
return await self._execute(
File "/home/python/venv/lib/python3.8/site-packages/asyncpg/connection.py", line 1625, in _execute
result, _ = await self.__execute(
File "/home/python/venv/lib/python3.8/site-packages/asyncpg/connection.py", line 1650, in __execute
return await self._do_execute(
File "/home/python/venv/lib/python3.8/site-packages/asyncpg/connection.py", line 1697, in _do_execute
result = await executor(stmt, None)
File "asyncpg/protocol/protocol.pyx", line 201, in bind_execute
asyncpg.exceptions.InvalidFunctionDefinitionError: return type mismatch in function declared to return text
DETAIL: Actual return type is information_schema.sql_identifier.
[2021-07-04 12:21:24 -0400] [18396] [ERROR] Traceback (most recent call last):
File "/home/python/venv/lib/python3.8/site-packages/starlette/routing.py", line 540, in lifespan
async for item in self.lifespan_context(app):
File "/home/python/venv/lib/python3.8/site-packages/starlette/routing.py", line 481, in default_lifespan
await self.startup()
File "/home/python/venv/lib/python3.8/site-packages/starlette/routing.py", line 516, in startup
await handler()
File "/home/python/venv/lib/python3.8/site-packages/easyauth/proxy.py", line 67, in db_setup
db = await Database.create(
File "/home/python/venv/lib/python3.8/site-packages/aiopyql/data.py", line 62, in create
await db.load_tables(db)
File "/home/python/venv/lib/python3.8/site-packages/aiopyql/postgres_connector.py", line 105, in load_tables
await db.get(show_tables_create)
File "/home/python/venv/lib/python3.8/site-packages/aiopyql/data.py", line 466, in get
result = await self.execute(query, commit=False)
File "/home/python/venv/lib/python3.8/site-packages/aiopyql/data.py", line 439, in execute
raise result
File "/home/python/venv/lib/python3.8/site-packages/aiopyql/data.py", line 393, in __process_queue
results = await self.process_query_no_commit(self, conn, query_id, query)
File "/home/python/venv/lib/python3.8/site-packages/aiopyql/postgres_connector.py", line 260, in process_query_no_commit
results = await conn.fetch(query)
File "/home/python/venv/lib/python3.8/site-packages/asyncpg/connection.py", line 583, in fetch
return await self._execute(
File "/home/python/venv/lib/python3.8/site-packages/asyncpg/connection.py", line 1625, in _execute
result, _ = await self.__execute(
File "/home/python/venv/lib/python3.8/site-packages/asyncpg/connection.py", line 1650, in __execute
return await self._do_execute(
File "/home/python/venv/lib/python3.8/site-packages/asyncpg/connection.py", line 1697, in _do_execute
result = await executor(stmt, None)
File "asyncpg/protocol/protocol.pyx", line 201, in bind_execute
asyncpg.exceptions.InvalidFunctionDefinitionError: return type mismatch in function declared to return text
DETAIL: Actual return type is information_schema.sql_identifier.

Issue with global store update?

Hello,

I've just deployed my Easyauth Server project in kubernetes. I have actually 2 pods running. But I don't know why, if I understood, it seems I have an issue with the global store update. The service token I generate do not share between pods. However when the pod start I saw this kind of logs

[client-api-extz-auth-server-cdc5b9d6b-88lwh] 03-16 08:46 EasyRpc-server /ws/manager WARNING  creating cron or get_downstream_registered_functions - interval 30 
[client-api-extz-auth-server-cdc5b9d6b-88lwh] 03-16 08:46 EasyRpc-server /ws/manager WARNING  global_store_update - update - tokens - add3d428-150f-43e9-9c3b-44d9a32f3cca 
[client-api-extz-auth-server-cdc5b9d6b-88lwh] 03-16 08:46 EasyRpc-server /ws/manager WARNING  global_store_update - update - tokens - add3d428-150f-43e9-9c3b-44d9a32f3cca 
[client-api-extz-auth-server-cdc5b9d6b-88lwh] 03-16 08:46 EasyRpc-server /ws/manager WARNING  global_store_update - update - tokens - 0ff378fb-21b0-4a12-8216-01c7143b1251 
[client-api-extz-auth-server-cdc5b9d6b-88lwh] 03-16 08:46 EasyRpc-server /ws/manager WARNING  global_store_update - update - tokens - 0ff378fb-21b0-4a12-8216-01c7143b1251 
[client-api-extz-auth-server-cdc5b9d6b-88lwh] 03-16 08:46 EasyRpc-server /ws/manager WARNING  global_store_update - update - tokens - 322be937-c3cd-45b5-9246-97c676b7b50c 
[client-api-extz-auth-server-cdc5b9d6b-88lwh] 03-16 08:46 EasyRpc-server /ws/manager WARNING  global_store_update - update - tokens - 322be937-c3cd-45b5-9246-97c676b7b50c 

That seems great doesn't it?
But when I call and endpoint which needs sepcific action sometimes it works, sometines not. Depending to which pod I'm redirected when I call my endpoint. Example:

{'header': {'error': {'message': "not authorized, permissions required: {'actions': ['CAN_CREATE_FREEMIUM_IA_CUSTOMER']}",
   'status': 403},
  'date': '2023-03-15T17:53:32.351900+00:00',
  'transaction_id': '35d4d14c-b385-4fc2-979f-8aac20b815e3',
  'request_url': 'http://xxx-yyy.com/v2/cus/create'}}

Thanks for your help,

Regards,

Rémy

"401 Unauthorized" when logging to admin account

I followed all instructions regarding installation and starting an EasyAuth server, but when trying to log in to "/admin" with the "admin" account that is automatically created when launching the server for the first time, I get error "401 Unauthorized". Any idea of what can be happening?

Update token expiry or refresh token

How i can refresh google auth token ?
If the user is active , i want to refresh the token and update the token expiry.
If in get_user i check the token time and call issue token again , will it work?

` def get_user_handler(request: Request):
if "token" not in request.cookies and "Authorization" not in request.headers:
return None

    if "token" in request.cookies and request.cookies['token'] != "INVALID":
        token = request.cookies['token']
        user_id = get_user_from_token(token)
        //Here i will check the expiry and if the expiry is less than 5 mins will  call **self.issue_token(permissions)**
        return  user_id

    elif "Authorization" in request.headers:
        # header should be separated by 'Bearer <tokenstr>'
        decoded_token = jwt.decode(
            request.headers["Authorization"].split(" ")[1],
            options={"verify_signature": False},
        )
        return decoded_token["permissions"]["users"][0]
    return None

return Depends(get_user_handler)`

Gui doesn't refresh

I am not sure if this is by design, but wanted to bring it to your attention. If you add anything via the gui, the page doesn't refresh. For example, if you click "Send test email" then click it again, it will show you the dialog of the previous test being submitted. Another example is adding a new group. You won't see the new group until you refresh the page manually.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.