forked from Github-Mirrors/canaille
Token revocation endpoint
This commit is contained in:
parent
a364b7ef1b
commit
c664259b52
5 changed files with 70 additions and 3 deletions
26
tests/test_token_revocation.py
Normal file
26
tests/test_token_revocation.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
from . import client_credentials
|
||||
|
||||
|
||||
def test_token_revocation(testclient, user, client, token, slapd_connection):
|
||||
assert not token.revoked
|
||||
|
||||
res = testclient.post(
|
||||
"/oauth/revoke",
|
||||
params=dict(token=token.oauthAccessToken,),
|
||||
headers={"Authorization": f"Basic {client_credentials(client)}"},
|
||||
)
|
||||
assert 200 == res.status_code
|
||||
assert {} == res.json
|
||||
|
||||
token.reload(slapd_connection)
|
||||
assert token.revoked
|
||||
|
||||
|
||||
def test_token_invalid(testclient, client):
|
||||
res = testclient.post(
|
||||
"/oauth/revoke",
|
||||
params=dict(token="invalid"),
|
||||
headers={"Authorization": f"Basic {client_credentials(client)}"},
|
||||
)
|
||||
assert 200 == res.status_code
|
||||
assert {} == res.json
|
|
@ -113,6 +113,14 @@ class LDAPObjectHelper:
|
|||
|
||||
return cls._attribute_type_by_name
|
||||
|
||||
def reload(self, conn=None):
|
||||
conn = conn or self.ldap()
|
||||
result = conn.search_s(self.dn, ldap.SCOPE_SUBTREE)
|
||||
self.changes = {}
|
||||
self.attrs = {
|
||||
k: [elt.decode("utf-8") for elt in v] for k, v in result[0][1].items()
|
||||
}
|
||||
|
||||
def save(self, conn=None):
|
||||
conn = conn or self.ldap()
|
||||
try:
|
||||
|
|
|
@ -182,9 +182,9 @@ class Token(LDAPObjectHelper, TokenMixin):
|
|||
@revoked.setter
|
||||
def revoked(self, value):
|
||||
if value:
|
||||
self.oauthRevoked = "true"
|
||||
self.oauthRevoked = "TRUE"
|
||||
else:
|
||||
self.oauthRevoked = "false"
|
||||
self.oauthRevoked = "FALSE"
|
||||
|
||||
def get_client_id(self):
|
||||
return self.oauthClientID
|
||||
|
|
|
@ -3,7 +3,7 @@ from flask import Blueprint, request, session, redirect
|
|||
from flask import render_template, jsonify, flash
|
||||
from flask_babel import gettext
|
||||
from .models import User, Client
|
||||
from .oauth2utils import authorization, IntrospectionEndpoint
|
||||
from .oauth2utils import authorization, IntrospectionEndpoint, RevocationEndpoint
|
||||
from .forms import LoginForm
|
||||
from .flaskutils import current_user
|
||||
|
||||
|
@ -61,3 +61,8 @@ def issue_token():
|
|||
@bp.route("/introspect", methods=["POST"])
|
||||
def introspect_token():
|
||||
return authorization.create_endpoint_response(IntrospectionEndpoint.ENDPOINT_NAME)
|
||||
|
||||
|
||||
@bp.route("/revoke", methods=["POST"])
|
||||
def revoke_token():
|
||||
return authorization.create_endpoint_response(RevocationEndpoint.ENDPOINT_NAME)
|
||||
|
|
|
@ -8,6 +8,7 @@ from authlib.oauth2.rfc6749.grants import (
|
|||
ClientCredentialsGrant,
|
||||
)
|
||||
from authlib.oauth2.rfc6750 import BearerTokenValidator as _BearerTokenValidator
|
||||
from authlib.oauth2.rfc7009 import RevocationEndpoint as _RevocationEndpoint
|
||||
from authlib.oauth2.rfc7636 import CodeChallenge
|
||||
from authlib.oauth2.rfc7662 import IntrospectionEndpoint as _IntrospectionEndpoint
|
||||
from authlib.oidc.core.grants import (
|
||||
|
@ -198,6 +199,32 @@ class BearerTokenValidator(_BearerTokenValidator):
|
|||
return token.revoked
|
||||
|
||||
|
||||
class RevocationEndpoint(_RevocationEndpoint):
|
||||
def query_token(self, token, token_type_hint, client):
|
||||
if token_type_hint == "access_token":
|
||||
return Token.filter(
|
||||
oauthClientID=client.oauthClientID, oauthAccessToken=token
|
||||
)
|
||||
elif token_type_hint == "refresh_token":
|
||||
return Token.filter(
|
||||
oauthClientID=client.oauthClientID, oauthRefreshToken=token
|
||||
)
|
||||
|
||||
item = Token.filter(oauthClientID=client.oauthClientID, oauthAccessToken=token)
|
||||
if item:
|
||||
return item[0]
|
||||
|
||||
item = Token.filter(oauthClientID=client.oauthClientID, oauthRefreshToken=token)
|
||||
if item:
|
||||
return item[0]
|
||||
|
||||
return None
|
||||
|
||||
def revoke_token(self, token):
|
||||
token.revoked = True
|
||||
token.save()
|
||||
|
||||
|
||||
class IntrospectionEndpoint(_IntrospectionEndpoint):
|
||||
def query_token(self, token, token_type_hint, client):
|
||||
if token_type_hint == "access_token":
|
||||
|
@ -252,3 +279,4 @@ def config_oauth(app):
|
|||
require_oauth.register_token_validator(BearerTokenValidator())
|
||||
|
||||
authorization.register_endpoint(IntrospectionEndpoint)
|
||||
authorization.register_endpoint(RevocationEndpoint)
|
||||
|
|
Loading…
Reference in a new issue