# Copyright (C) 2018 Lars Wirzenius # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import Crypto.PublicKey.RSA import jwt import muck class TokenChecker: def __init__(self, signing_key_pub): pubkey = Crypto.PublicKey.RSA.importKey(signing_key_pub) self._key = pubkey.exportKey('OpenSSH') def parse_header(self, value): token = self._get_token_text(value) options = { 'verify_aud': False, } try: return jwt.decode( token, key=self._key, audience=None, options=options) except (jwt.DecodeError, jwt.ExpiredSignatureError) as e: raise muck.Error(str(e)) def _get_token_text(self, value): if not isinstance(value, str): raise muck.Error('Header does not have a string value') if not value: raise muck.Error('Header does not have a non-empty string value') words = value.split() if len(words) != 2: raise muck.Error('Header does not consist of two words') if words[0].lower() != 'bearer': raise muck.Error('Header does not start with "Bearer"') return words[1] def create_token(claims, key_text): key = Crypto.PublicKey.RSA.importKey(key_text) token = jwt.encode(claims, key.exportKey('PEM'), algorithm='RS512') return token.decode('ascii') test_key_text = '''\ -----BEGIN RSA PRIVATE KEY----- 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 -----END RSA PRIVATE KEY----- '''