mcueto / djangorestframework-auth0

Library to simply use Auth0 token authentication in DRF within djangorestframework-jwt
MIT License
91 stars 19 forks source link

Debugging djangorestframework-auth0 #53

Closed toinbis closed 4 years ago

toinbis commented 4 years ago

Hi,

I have a setup that works with rest_framework_jwt, yet rest_framework_auth0 returns 401 unauthorize.

My settings.py:

<...>
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # 'django.contrib.sites',
    'social_django',
 #   'auth0login',
    'core',
    'debug_toolbar',
    #'whitenoise.runserver_nostatic',
    'rest_framework',
    'corsheaders',
    'rest_framework.authtoken',
    'rest_framework_jwt',
    'rest_framework_auth0',
]
<...>
MIDDLEWARE = [
    'core.middlewares.SimpleMiddleware',#log http requests to console
    'corsheaders.middleware.CorsMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',

    # You need to define a way to map the username from the 
    # Access Token payload to the Django authentication system user.
    'django.contrib.auth.middleware.RemoteUserMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'debug_toolbar.middleware.DebugToolbarMiddleware'
]
<...>

AUTH0_DOMAIN = '<brand>.auth0.com'
API_IDENTIFIER = 'https://api.<brand>.com/v1/'
PUBLIC_KEY = None
JWT_ISSUER = None

if AUTH0_DOMAIN:
    jsonurl = request.urlopen('https://' + AUTH0_DOMAIN + '/.well-known/jwks.json')
    jwks = json.loads(jsonurl.read().decode('utf-8'))
    cert = '-----BEGIN CERTIFICATE-----\n' + jwks['keys'][0]['x5c'][0] + '\n-----END CERTIFICATE-----'
    certificate = load_pem_x509_certificate(cert.encode('utf-8'), default_backend())
    PUBLIC_KEY = certificate.public_key()
    JWT_ISSUER = 'https://' + AUTH0_DOMAIN + '/'

AUTH0 = {
  'CLIENTS': {
      'default': {
          'AUTH0_CLIENT_ID': 'BNiKZbeNxqLTQeV4Jd7SfOS52l4fYbHl',
          'AUTH0_AUDIENCE': 'https://api.<brand>.com/v1/', #From Audience
          'AUTH0_ALGORITHM': 'RS256',  # default used in Auth0 apps
          'PUBLIC_KEY': PUBLIC_KEY
      }
}}

def jwt_get_username_from_payload_handler(payload):
    return 'auth0user'

JWT_AUTH = {
    'JWT_PAYLOAD_GET_USERNAME_HANDLER': jwt_get_username_from_payload_handler,
    'JWT_PUBLIC_KEY': PUBLIC_KEY,
    'JWT_ALGORITHM': 'RS256',
    'JWT_AUDIENCE': API_IDENTIFIER,
    'JWT_ISSUER': JWT_ISSUER,
    'JWT_AUTH_HEADER_PREFIX': 'Bearer',
}

CORS_ORIGIN_WHITELIST = (
    'http://localhost:8080', 'http://127.0.0.1:8080', 'http://127.0.0.1:3000', 'http://localhost:3000'
)

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_auth0.authentication.Auth0JSONWebTokenAuthentication',
        #'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        # 'rest_framework.authentication.SessionAuthentication',
        # 'rest_framework.authentication.BasicAuthentication',
    ),   
}

I make API call from authorized SPA application(vue.js), with rest_framework_jwt.authentication.JSONWebTokenAuthentication as a single element in DEFAULT_AUTHENTICATION_CLASSES HTTP Request/Response looks as follows:

Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
GET HTTP/1.1
Content-Length: 
Content-Type: text/plain
Host: 127.0.0.1:8000
Connection: keep-alive
Accept: application/json, text/plain, */*
Sec-Fetch-Dest: empty
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik5VTTVNVGhGTjBaRU1USkNRVVZHT0RWQ00wUXdNRGd5TWtFek1rUkJOVVZGTXpReU5rRTRRZyJ9.eyJpc3MiOiJodHRwczovL25hYnRpdmUuYXV0aDAuY29tLyIsInN1YiI6Imdvb2dsZS1vYXV0aDJ8MTE2MTk4NTk0NjcyNzczMjIwMjk3IiwiYXVkIjpbImh0dHBzOi8vYXBpLm5hYnRpdmUuY29tL3YxLyIsImh0dHBzOi8vbmFidGl2ZS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNTg1MDM3NzY5LCJleHAiOjE1ODUxMjQxNjksImF6cCI6IkJOaUtaYmVOeHFMVFFlVjRKZDdTZk9TNTJsNGZZYkhsIiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCJ9.EWKKslf_qhWlQaRnp7ofGH8eAI7Hqe17bF4Z4o_GiTN-S2WfTVrWhmXb49rrRJq1ktXdgF1k5LeEU4aYIxC_3ZktcKqiKu-WSJdHrf6OtfNHrkaiWz9UqJo9YI9tYZ7FdQz_t9zi3ZeyPhdLRwHy_KbwjvmDWUXSgMNufpm8anomrEbLzEez60Ab8t8ABhaZ2v_t4B-VyTFCGrz7JK47mNuh_qirgCVRAJen0XiPAGzR_k8iJBHa--zlZE6QydVsk2j9sWcTrtDeAVbqh3KaEfiINn98R6lMXyhp4msBGwID2kWa6hb87LzQyqx3nf-qvXNc7tR-KleqO(trimmed for security purposes)
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
Origin: http://127.0.0.1:3000
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Referer: http://127.0.0.1:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8

b''
[24/Mar/2020 08:16:10] "GET /v1/private/ HTTP/1.1" 200 53

Yet if I swap to rest_framework_auth0.authentication.Auth0JSONWebTokenAuthentication as a single element in DEFAULT_AUTHENTICATION_CLASSES, the result is:

Quit the server with CONTROL-C.
GET HTTP/1.1
Content-Length: 
Content-Type: text/plain
Host: 127.0.0.1:8000
Connection: keep-alive
Accept: application/json, text/plain, */*
Sec-Fetch-Dest: empty
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik5VTTVNVGhGTjBaRU1USkNRVVZHT0RWQ00wUXdNRGd5TWtFek1rUkJOVVZGTXpReU5rRTRRZyJ9.eyJpc3MiOiJodHRwczovL25hYnRpdmUuYXV0aDAuY29tLyIsInN1YiI6Imdvb2dsZS1vYXV0aDJ8MTE2MTk4NTk0NjcyNzczMjIwMjk3IiwiYXVkIjpbImh0dHBzOi8vYXBpLm5hYnRpdmUuY29tL3YxLyIsImh0dHBzOi8vbmFidGl2ZS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNTg1MDM4MDMyLCJleHAiOjE1ODUxMjQ0MzIsImF6cCI6IkJOaUtaYmVOeHFMVFFlVjRKZDdTZk9TNTJsNGZZYkhsIiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCJ9.SHxGJcCWhGXUMZ25J8Hb1jV5FvU-xfQG0bskObRVkYvxrjtZ5pV8wKigtW7kb2kDefiZOMLYJuMlISX2pkFfRbe3buDbRX8W6xvszBX2j4LCRO4vR9SELmdIt7-1PL2QsDNq7HMLc666SWwz-NkGNc3qrL9FpQU7p-NdfoiKXYuSthsiPtxsgaEtnDTgq76Ree1ZTQp91-NcqS8gJVuFWHX1wr6yEYmOxs_P_PzAeRuueRc-JI88dEbmbowu4_X-UbviQjXAReJB_n0Uv4MkWWVNF2C-_CcJH_4QEXczsIcIsXMHangd9qkdY6688(trimmed for security purposes)
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
Origin: http://127.0.0.1:3000
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Referer: http://127.0.0.1:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8

b''
Unauthorized: /v1/private/
[24/Mar/2020 08:20:33] "GET /v1/private/ HTTP/1.1" 401 58

Maybe you can spot the problem in my config? Or probably you can guide me what would be the standard way to debug such situation. Played a bit with logger but could not manage to get it printing all the steps.

Other than that - thanks for cool lib. I intend to use it because I prefer user to be created in Django upon first auth. I also very keen to fully understand how the Roles/Permissions are validate via management API, not sure how will use that yet this seems to give more control/flexibility.

Best, Tom

mcueto commented 4 years ago

Hi @toinbin i have no time to look at it at the moment bu i will try to help you ASAP

I hope you're dealing well with the CV

toinbis commented 4 years ago

Hi @mcueto - no rush at all. Ended up writting my own custom Auth class which was basically 1:1.01 copy of this repo content. All works now :) I think we can close this particular ticket. But before closing - I wanted to ask how are you dealing with delays of auth response caused by additional call to auth0 management API? You consider that not substantial to bug users? Cause as of now I'm thinking of some workarounds - maybe doing the initial call and then syncing permissions/roles/groups in the background, with webhooks or any way I can think of that doesn't require this call to management API each time user is authenticating.

Thanks again for great library! P.S. Just now realized what CV stands for :D Today is the last day of 14days mandatory home-isolation after returning from Thailand :D Hope you're doing well in this front as well!

mcueto commented 4 years ago

I will deal with all the lag when memcached is implemented ( #52 ) so every developer can set how the cache config would adapt to each business model.

You couldn't use this library from pip because the tag published there is not the same code as the repo, i recommend you to install this package using pip install git+git://github.com/mcueto/djangorestframework-auth0.git meanwhile(i'll update the package on pypi once memcached is implemented)

i'm doing pretty well, self isolated and limiting the contact with others at a minimum(go to the grocery store near my home).

toinbis commented 4 years ago

Oh, I haven't seen this ticket! Thanks for letting me know.

Also thanks for update on the pip status as well, much appreciated. Will be watching this repo and surely waiting for updates on #52.

Putting code aside - am really glad you are doing well! Let's stay this way! :)

WIll talk in other tickets, am closing this one to issues list neat and clean!