*: add support for bearer authentication on API

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-04-13 19:57:33 +02:00
parent 513d3c1c31
commit 6821679fbc
6 changed files with 31 additions and 28 deletions

View file

@ -10,29 +10,29 @@ from structlog.stdlib import get_logger
from authentik.core.models import Token, TokenIntents, User
LOGGER = get_logger()
X_AUTHENTIK_PREVENT_BASIC_HEADER = "HTTP_X_AUTHENTIK_PREVENT_BASIC"
def token_from_header(raw_header: bytes) -> Optional[Token]:
"""raw_header in the Format of `Basic dGVzdDp0ZXN0`"""
auth_credentials = raw_header.decode()
# Accept headers with Type format and without
if " " in auth_credentials:
auth_type, auth_credentials = auth_credentials.split()
if auth_type.lower() != "basic":
LOGGER.debug(
"Unsupported authentication type, denying", type=auth_type.lower()
)
return None
try:
auth_credentials = b64decode(auth_credentials.encode()).decode()
except (UnicodeDecodeError, Error):
if " " not in auth_credentials:
return None
# Accept credentials with username and without
if ":" in auth_credentials:
_, password = auth_credentials.split(":")
else:
password = auth_credentials
auth_type, auth_credentials = auth_credentials.split()
if auth_type.lower() not in ["basic", "bearer"]:
LOGGER.debug("Unsupported authentication type, denying", type=auth_type.lower())
return None
password = auth_credentials
if auth_type.lower() == "basic":
try:
auth_credentials = b64decode(auth_credentials.encode()).decode()
except (UnicodeDecodeError, Error):
return None
# Accept credentials with username and without
if ":" in auth_credentials:
_, password = auth_credentials.split(":")
else:
password = auth_credentials
if password == "": # nosec
return None
tokens = Token.filter_not_expired(key=password, intent=TokenIntents.INTENT_API)
@ -43,10 +43,10 @@ def token_from_header(raw_header: bytes) -> Optional[Token]:
class AuthentikTokenAuthentication(BaseAuthentication):
"""Token-based authentication using HTTP Basic authentication"""
"""Token-based authentication using HTTP Bearer authentication"""
def authenticate(self, request: Request) -> Union[tuple[User, Any], None]:
"""Token-based authentication using HTTP Basic authentication"""
"""Token-based authentication using HTTP Bearer authentication"""
auth = get_authorization_header(request)
token = token_from_header(auth)
@ -56,6 +56,4 @@ class AuthentikTokenAuthentication(BaseAuthentication):
return (token.user, None)
def authenticate_header(self, request: Request) -> str:
if X_AUTHENTIK_PREVENT_BASIC_HEADER in request._request.META:
return ""
return 'Basic realm="authentik"'
return "Bearer"

View file

@ -11,7 +11,7 @@ from authentik.core.models import Token, TokenIntents
class TestAPIAuth(TestCase):
"""Test API Authentication"""
def test_valid(self):
def test_valid_basic(self):
"""Test valid token"""
token = Token.objects.create(
intent=TokenIntents.INTENT_API, user=get_anonymous_user()
@ -19,6 +19,13 @@ class TestAPIAuth(TestCase):
auth = b64encode(f":{token.key}".encode()).decode()
self.assertEqual(token_from_header(f"Basic {auth}".encode()), token)
def test_valid_bearer(self):
"""Test valid token"""
token = Token.objects.create(
intent=TokenIntents.INTENT_API, user=get_anonymous_user()
)
self.assertEqual(token_from_header(f"Bearer {token.key}".encode()), token)
def test_invalid_type(self):
"""Test invalid type"""
self.assertIsNone(token_from_header("foo bar".encode()))

View file

@ -143,7 +143,7 @@ SWAGGER_SETTINGS = {
"authentik.api.pagination_schema.PaginationInspector",
],
"SECURITY_DEFINITIONS": {
"token": {"type": "apiKey", "name": "Authorization", "in": "header"}
"Bearer": {"type": "apiKey", "name": "Authorization", "in": "header"}
},
}

View file

@ -2,7 +2,6 @@ package ak
import (
"crypto/tls"
"encoding/base64"
"fmt"
"net/http"
"net/url"
@ -20,7 +19,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
pathTemplate := "%s://%s/ws/outpost/%s/"
scheme := strings.ReplaceAll(pbURL.Scheme, "http", "ws")
authHeader := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("Basic :%s", ac.token)))
authHeader := fmt.Sprintf("Bearer %s", ac.token)
header := http.Header{
"Authorization": []string{authHeader},

View file

@ -13,12 +13,12 @@ consumes:
produces:
- application/json
securityDefinitions:
token:
Bearer:
type: apiKey
name: Authorization
in: header
security:
- token: []
- Bearer: []
paths:
/admin/apps/:
get:

View file

@ -16,7 +16,6 @@ export const DEFAULT_CONFIG = new Configuration({
basePath: "/api/v2beta",
headers: {
"X-CSRFToken": getCookie("authentik_csrf"),
"X-Authentik-Prevent-Basic": "true"
},
middleware: [
API_DRAWER_MIDDLEWARE,