Skip to content

Commit

Permalink
Merge pull request #92 from GabrielInTheWorld/of-can-access-mediafiles
Browse files Browse the repository at this point in the history
Python-library: Can authenticate only with cookie now
  • Loading branch information
GabrielInTheWorld committed Aug 4, 2021
2 parents c2b69fa + 0bd06ca commit 205a833
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 9 deletions.
17 changes: 17 additions & 0 deletions auth/libraries/pip-auth/authlib/auth_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ def __init__(self, debug_fn: Any = print) -> None:
def authenticate(
self, access_token: Optional[str], refresh_id: Optional[str]
) -> Tuple[int, Optional[str]]:
"""
Tries to check and read a user_id from a given access_token and refresh_id.
"""
self.debug_fn(f"Try to authenticate with")
self.debug_fn(f"AccessToken: {access_token}")
self.debug_fn(f"RefreshId: {refresh_id}")
Expand All @@ -31,6 +34,20 @@ def authenticate(
return ANONYMOUS_USER, None
return self.validator.verify(access_token, refresh_id)

def authenticate_only_refresh_id(self, refresh_id: Optional[str]) -> int:
"""
This tries to check and read a user_id from a given refresh_id. It only returns an int or raises an error.
Use this with caution, because using only a refresh_id to verify a valid authentication is vulnerable
for CSRF-attacks.
"""
self.debug_fn("Try to authenticate only with")
self.debug_fn(f"RefreshId: {refresh_id}")
if not refresh_id:
self.debug_fn("No refresh_id given")
return ANONYMOUS_USER
return self.validator.verify_only_cookie(refresh_id)

def hash(self, to_hash: str) -> str:
self.debug_fn(f"Hash {to_hash}: {self.hashing_handler.hash(to_hash)}")
return self.hashing_handler.hash(to_hash)
Expand Down
7 changes: 4 additions & 3 deletions auth/libraries/pip-auth/authlib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

from .exceptions import KeyException

AUTH_DEV_KEY = "auth-dev-key"
AUTH_DEV_TOKEN_KEY = "auth-dev-token-key"
AUTH_DEV_COOKIE_KEY = 'auth-dev-cookie-key'
DEVELEOPMENT_VARIABLE = "OPENSLIDES_DEVELOPMENT"
VERBOSE_TRUE_FIELDS = ["y", "1", "yes", "true", "on"]

Expand All @@ -17,8 +18,8 @@ def __init__(self, debug_fn: Any = print) -> None:
def __load_keys(self) -> None:
self.debug_fn("Environment.__load_keys")
if self.is_dev_mode():
self.auth_token_key = AUTH_DEV_KEY
self.auth_cookie_key = AUTH_DEV_KEY
self.auth_token_key = AUTH_DEV_TOKEN_KEY
self.auth_cookie_key = AUTH_DEV_COOKIE_KEY
else:
token_secret_path = "/run/secrets/auth_token_key"
self.auth_token_key = self.read_file(token_secret_path)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import jwt

from .base import BaseTestEnvironment
from ..config import Environment
from ..constants import USER_ID_PROPERTY
from datetime import datetime

class TestAuthenticateOnlyCookie(BaseTestEnvironment):
environment = Environment()

def test_authenticate_with_cookie(self):
_, cookie = self.fake_request.login()
user_id = self.auth_handler.authenticate_only_refresh_id(cookie)
self.assertEqual(1, user_id)

def test_authenticate_without_cookie(self):
user_id = self.auth_handler.authenticate_only_refresh_id(None)
self.assertEqual(0, user_id)

def test_authenticate_without_cookie_but_token(self):
access_token, _ = self.fake_request.login()
with self.assertRaises(jwt.exceptions.InvalidSignatureError):
self.auth_handler.authenticate_only_refresh_id(access_token)

def test_authenticate_without_cookie_but_request(self):
self.fake_request.login()
user_id = self.auth_handler.authenticate_only_refresh_id(None)
self.assertEqual(0, user_id)

def test_authenticate_with_expired_cookie(self):
_, cookie = self.fake_request.login()
cookie = cookie[7:]

session_id = jwt.decode(cookie, self.environment.get_cookie_key(), algorithms=["HS256"])["sessionId"]
expired_cookie_payload = {
"sessionId": session_id,
USER_ID_PROPERTY: 1,
"exp": datetime.utcfromtimestamp(0)
}
raw_cookie = jwt.encode(expired_cookie_payload, self.environment.get_cookie_key(), algorithm="HS256")
expired_cookie = "bearer " + raw_cookie.decode("utf-8")
with self.assertRaises(jwt.exceptions.ExpiredSignatureError):
self.auth_handler.authenticate_only_refresh_id(expired_cookie)
23 changes: 23 additions & 0 deletions auth/libraries/pip-auth/authlib/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ def __init__(self, http_handler: HttpHandler, debug_fn: Any = print) -> None:
def verify(
self, token_encoded: str, cookie_encoded: str
) -> Tuple[int, Optional[str]]:
"""
This receives encoded jwts contained in a cookie and a token.
Then, it verifies, that the jwts are wellformed and still valid.
Afterwards, this returns a user_id read from the decoded jwt contained in the token.
"""
self.debug_fn("Validator.verify")
try:
self.__assert_instance_of_encoded_jwt(token_encoded, "Token")
Expand All @@ -43,6 +48,24 @@ def verify(
except jwt.exceptions.InvalidSignatureError:
raise InvalidCredentialsException("The signature of the jwt is invalid")

def verify_only_cookie(self, cookie_encoded: str) -> int:
"""
This receives only an encoded jwt contained in a cookie and verifies, that
the jwt is wellformed and still valid.
Afterwards, this returns a user_id read from the decoded jwt contained in the cookie.
It only returns an int or raises an error.
Use this with caution, because using only a cookie to verify a valid authentication is vulnerable
for CSRF-attacks.
"""
self.debug_fn("Validator.verify_only_cookie")
cookie_encoded = self.__get_jwt_from_bearer_jwt(cookie_encoded, "cookie")
cookie = self.__decode(cookie_encoded, self.environment.get_cookie_key())
user_id = cookie.get(USER_ID_PROPERTY)
if not isinstance(user_id, int):
raise AuthenticateException("user_id is not an int")
return user_id

def __verify_ticket(self, token_encoded: str, cookie_encoded: str) -> int:
self.debug_fn("Validator.__verify_ticket")
token_encoded = self.__get_jwt_from_bearer_jwt(token_encoded, "token")
Expand Down
7 changes: 4 additions & 3 deletions auth/src/api/services/key-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { KeyException } from '../../core/exceptions/key-exception';
import { KeyHandler } from '../interfaces/key-handler';
import { Logger } from './logger';

const AUTH_DEV_KEY = 'auth-dev-key';
const AUTH_DEV_TOKEN_KEY = 'auth-dev-token-key';
const AUTH_DEV_COOKIE_KEY = 'auth-dev-cookie-key';

export class KeyService extends KeyHandler {
protected tokenKey = '';
Expand All @@ -29,8 +30,8 @@ export class KeyService extends KeyHandler {
private loadKeys(): void {
Logger.debug('KeyService.loadKeys -- is in dev-mode:', Config.isDevMode());
if (Config.isDevMode()) {
this.tokenKey = AUTH_DEV_KEY;
this.cookieKey = AUTH_DEV_KEY;
this.tokenKey = AUTH_DEV_TOKEN_KEY;
this.cookieKey = AUTH_DEV_COOKIE_KEY;
} else {
const tokenKeyPath = '/run/secrets/auth_token_key';
this.tokenKey = this.readFile(tokenKeyPath);
Expand Down
9 changes: 6 additions & 3 deletions auth/src/api/services/ticket-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class TicketService extends TicketHandler {
return { isValid: false, message: 'User is empty.' };
}
const session = await this.sessionHandler.addSession(user);
const cookie = this.generateCookie(session);
const cookie = this.generateCookie(session, user);
const token = this.generateToken(session, user);
return { isValid: true, message: 'successful', result: { cookie, token, user } };
}
Expand Down Expand Up @@ -180,8 +180,11 @@ export class TicketService extends TicketHandler {
return `bearer ${token}`;
}

private generateCookie(sessionId: string): string {
const cookie = jwt.sign({ sessionId }, this.cookieKey, { expiresIn: '1d', algorithm: 'HS256' });
private generateCookie(sessionId: string, user: User): string {
const cookie = jwt.sign({ sessionId, userId: user.id }, this.cookieKey, {
expiresIn: '1d',
algorithm: 'HS256'
});
return `bearer ${cookie}`;
}
}

0 comments on commit 205a833

Please sign in to comment.