def getPrivateKey(self, kid=None): """Get private key :param str kid: key ID :return: S_OK(obj)/S_ERROR() """ result = self.getActiveKeys(kid) if not result["OK"]: return result jwks = result["Value"] if kid: strkey = jwks[0]["key"] return S_OK(JsonWebKey.import_key(json.loads(jwks[0]["key"]))) newer = {} for jwk in jwks: if int(jwk["expires_at"]) > int( newer.get("expires_at", time.time() + (24 * 3600))): newer = jwk if not newer.get("key"): result = self.generateRSAKeys() if not result["OK"]: return result newer = result["Value"] return S_OK(JsonWebKey.import_key(json.loads(newer["key"])))
def __init__(self): rsa = RSAKey.generate_key(key_size=4096, is_private=True) self.jwk_public_key = JsonWebKey.import_key(rsa.as_pem(is_private=False)) self.jwk_private_key = JsonWebKey.import_key(rsa.as_pem(is_private=True)) self._key_alg = 'RSA-OAEP-256' self._content_alg = 'A256GCM' self._compression_alg = 'DEF'
def test_import_keys(self): rsa_pub_pem = read_file_path('rsa_public.pem') self.assertRaises(ValueError, JsonWebKey.import_key, rsa_pub_pem, {'kty': 'EC'}) key = JsonWebKey.import_key(raw=rsa_pub_pem, options={'kty': 'RSA'}) self.assertIn('e', dict(key)) self.assertIn('n', dict(key)) key = JsonWebKey.import_key(raw=rsa_pub_pem) self.assertIn('e', dict(key)) self.assertIn('n', dict(key))
def encrypted_script_response( self, opts: vpb.ExecuteScriptRequest.EncryptionOptions ) -> vpb.ExecuteScriptResponse: ''' Returns a script repsonse with encrypted row batch fields, if they exist, and if the options are set. ''' es_resp = self.execute_script_response if not es_resp.HasField("data"): return es_resp if opts is None: return es_resp # Now we encrypt the batch. rb = es_resp.data.batch.SerializeToString() key = JsonWebKey.import_key(json.loads(opts.jwk_key)) encrypted_batch = JsonWebEncryption().serialize_compact( { 'alg': opts.key_alg, 'enc': opts.content_alg, 'zip': opts.compression_alg, }, rb, key) # Make sure we only send the encrypted_batch through. es_resp.data.ClearField('batch') es_resp.data.encrypted_batch = encrypted_batch return es_resp
def _load_keys_from_url(url, verify=True): """ Expects something on this form: {"keys": [ { "kty":"EC", "crv":"P-256", "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", "use":"enc", "kid":"1" }, { "kty":"RSA", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFb....." "e":"AQAB", "kid":"2011-04-29" } ] } """ keys = [] r = request("GET", url, allow_redirects=True, verify=verify) if r.status_code == 200: keys_dict = json.loads(r.text) for key_spec in keys_dict["keys"]: key = JsonWebKey.import_key(key_spec) keys.append(key) else: raise Exception("Error loading JWK set - HTTP GET error: %s" % r.status_code) return keys
def userinfo(): req = flask.request log_request('GET-USERINFO', req) access_token = req.headers.get('Authorization', None) # TODO: if not access_token # TODO: Validate access-token log.info("GET-USERINFO: Access token: '{}'".format(access_token)) access_token_parts = access_token.split() if access_token_parts[0].lower() != 'bearer' or len(access_token_parts) != 2: return flask.render_template('error.html', text='Invalid authorization') access_token = access_token_parts[1] # FIXME with open('jwt-key.pub', 'rb') as f: key_data = f.read() pub_key = JsonWebKey.import_key(key_data, {'kty': 'RSA'}) access_token_json = jwt.decode(access_token, pub_key) scope = access_token_json['scope'] # TODO: Validate audience in access token covers /userinfo log.info("GET-USERINFO: Access token audience: '{}'".format(access_token_json['aud'])) log.info("GET-USERINFO: Scope '{}'".format(scope)) claims = dict() # See https://openid.net/specs/openid-connect-basic-1_0.html#StandardClaims for what claims to include in access token if 'profile' in scope: claims['name'] = 'Name of user is {}'.format(access_token_json['sub'].capitalize()) return claims
def _create_token( self, type_token: str, exp_time: Optional[int], *, custom_claims, ) -> str: # Data section claims = { "iat": timezone.now(), "nbf": timezone.now(), "jti": str(uuid.uuid4()), "type": type_token, **custom_claims, } if exp_time: claims["exp"] = exp_time if self.issuer: claims["iss"] = self.issuer if self.audience: claims["aud"] = self.audience key = JsonWebKey.import_key(self.private_key) headers = {"alg": "RS256", "kid": key.thumbprint()} return jwt.encode(header=headers, payload=claims, key=key)
def dummy_key_pair(): key = ec.generate_private_key(ec.SECP256R1(), backend=default_backend()) private_key = key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption()) public_key = key.public_key().public_bytes( serialization.Encoding.OpenSSH, serialization.PublicFormat.OpenSSH) PRIVATE_KEY = JsonWebKey.import_key(private_key, {'kty': 'EC'}) PUBLIC_KEY = JsonWebKey.import_key(public_key, {'kty': 'EC'}) keys = {'PUBLIC_KEY': PUBLIC_KEY, 'PRIVATE_KEY': PRIVATE_KEY} return keys
def _validate(self): """ Reference: https://docs.docker.com/registry/spec/manifest-v2-1/#signed-manifests """ if not self._signatures: return payload_str = self._payload for signature in self._signatures: protected = signature[DOCKER_SCHEMA1_PROTECTED_KEY] sig = signature[DOCKER_SCHEMA1_SIGNATURE_KEY] jwk = JsonWebKey.import_key( signature[DOCKER_SCHEMA1_HEADER_KEY]["jwk"]) jws = JsonWebSignature( algorithms=[signature[DOCKER_SCHEMA1_HEADER_KEY]["alg"]]) obj_to_verify = { DOCKER_SCHEMA1_PROTECTED_KEY: protected, DOCKER_SCHEMA1_SIGNATURE_KEY: sig, DOCKER_SCHEMA1_HEADER_KEY: { "alg": signature[DOCKER_SCHEMA1_HEADER_KEY]["alg"] }, "payload": base64url_encode(payload_str), } try: data = jws.deserialize_json(obj_to_verify, jwk.get_public_key()) except (BadSignatureError, UnsupportedAlgorithmError): raise InvalidSchema1Signature() if not data: raise InvalidSchema1Signature()
def get_jwk(): LOGGER.debug('Loading jwk from public key...') key_data = None with open(app_context().config['jwk_public_key_path'], 'rb') as _key_file: key_data = _key_file.read() LOGGER.debug(key_data) key = JsonWebKey.import_key(key_data, {'kty': 'RSA'}) return {'keys': [{**key.as_dict(), 'kid': 'demo_key'}]}
def refresh_token(): req = flask.request session_cookie = req.cookies.get(SESSION_COOKIE_NAME) session_id = session_cookie if session_id in sessions: session = sessions[session_id] else: return flask.make_response(flask.redirect(own_url, code=303)) log.info('Refresh token, session {}'.format(session_id)) data = { 'refresh_token': session['refresh_token'], 'grant_type': 'refresh_token' } headers = { 'Authorization': 'Basic ' + encode_client_creds(client_id, client_secret), 'Content-type': 'application/x-www-form-urlencoded' } log.info("Refresh token from url: '{}'".format(oauth2_token_url)) response = requests.post(oauth2_token_url, data=data, headers=headers) log_response('REFRESH-TOKEN', response) if response.status_code != 200: return 'Failed with status {}: {}'.format(response.status_code, response.text) response_json = response.json() for token_type in ['id_token', 'access_token', 'refresh_token']: if token_type in response_json: log.info("Got {} token '{}'".format(token_type, response_json[token_type])) if 'id_token' in response_json: id_token = response_json['id_token'] token_pub_jwk_json = token_get_jwk(id_token) token_pub_jwk = JsonWebKey.import_key(token_pub_jwk_json) claims = jwt.decode(id_token, token_pub_jwk) session['id_token'] = id_token, session['id_token_claims'] = claims if 'access_token' in response_json: session['access_token'] = response_json['access_token'] if 'refresh_token' in response_json: session['refresh_token'] = response_json['refresh_token'] sessions[session_cookie] = session resp = flask.make_response(flask.redirect(own_url, code=303)) return resp
def test_03_keyid(self): print("\n-----", sys._getframe().f_code.co_name, "-----") pem1 = "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIDSt1IOhS5ZmY6nkX/Wh7pT+Y45TmYxrwoc1pG72v387oAoGCCqGSM49\nAwEHoUQDQgAEdEsjD2i2LytHOjNxxc9PbFeqQ89aMLOfmdBbEoSOhZBukJ52EqQM\nhOdgHqyqD4hEyYxgDu3uIbKat+lEZEhb3Q==\n-----END EC PRIVATE KEY-----" keypair1 = bbclib.KeyPair() keypair1.mk_keyobj_from_private_key_pem(pem1) keyid1 = keypair1.get_key_id() pubkey = keypair1.get_public_key_in_pem() obj = JsonWebKey.import_key(pubkey, {'kty': 'EC'}) obj["kty"] = "EC" json_obj = json.dumps(obj, separators=(',', ':'), sort_keys=True) keyid2 = hashlib.sha256(json_obj.encode()).digest() assert keyid1 == keyid2
def getKeySet(self): """Get key set :return: S_OK(obj)/S_ERROR() """ result = self.getActiveKeys() if result["OK"] and not result["Value"]: result = self.generateRSAKeys() if result["OK"]: result["Value"] = [result["Value"]] if not result["OK"]: return result return S_OK( KeySet([ JsonWebKey.import_key(json.loads(key["key"])) for key in result["Value"] ]))
def setUp(self): os.environ['GOOGLE_CLIENT_ID'] = ( '81427209327-c9674sudcht4aah93bkbpnl0' '1np7fake.apps.googleusercontent.com') os.environ['GOOGLE_CLIENT_SECRET'] = 'Xv-r5yOgDCquIUMqt33Rfake' fixture_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data') self.code = ('4%2F0AY0e-g5kPv0ekCxPxaiUnrggLHmgNRPhA' 'PwrUQkXJSpfv4NtBf1Ck7_gMdo6ZWpV3Mfake') self.nonce = generate_token() self.privatekey = open(os.path.join(fixture_path, 'jwt.pem'), 'rb').read() self.publickey = open(os.path.join(fixture_path, 'jwt.pub'), 'rb').read() self.openid_cert = dict(kid='69ed57f424491282a18020fd585954b70bb45ae0', **JsonWebKey.import_key( self.publickey, dict(kty='RSA')).as_dict()) self.scope = ('email+profile+openid+https%3A%2F%2Fwww.googleapis.com' '%2Fauth%2Fuserinfo.email+https%3A%2F' '%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile') with open(os.path.join(fixture_path, 'oauth2_response.yaml'), 'rt', encoding='utf8') as f: self.data = yaml.safe_load(f) httpretty.enable(allow_net_connect=False) httpretty.register_uri( httpretty.GET, 'https://accounts.google.com/.well-known/openid-configuration', body=json.dumps(self.data['openid_google']), content_type='application/json') httpretty.register_uri( httpretty.GET, self.data['openid_google']['authorization_endpoint'], status=302, body='') httpretty.register_uri(httpretty.GET, self.data['openid_google']['jwks_uri'], body=json.dumps([self.openid_cert]), content_type='application/json') oauth2_init(self.app)
def jwk_dict_to_public_key(jwk_dict): """ Converts the specified JWK into a public key. """ jwk = JsonWebKey.import_key(jwk_dict) if isinstance(jwk, RSAKey): rsa_pk = jwk.as_key() return RSAPublicNumbers(e=rsa_pk.public_numbers().e, n=rsa_pk.public_numbers().n).public_key( default_backend()) elif isinstance(jwk, ECKey): ec_pk = jwk.as_key() return EllipticCurvePublicNumbers( x=ec_pk.public_numbers().x, y=ec_pk.public_numbers().y, curve=ec_pk.public_numbers().curve, ).public_key(default_backend()) raise Exception("Unsupported kind of JWK: %s", str(type(jwk)))
def get_jwk(): def get_timestamp_from_name(key_name): start = key_name.rfind('/') end = key_name.rfind('.key.pub') return key_name[start + 1:end] jwt_key_res = {'keys': []} pub_key_list = glob.glob('/webpage/jwks/*.key.pub') for pub_key in pub_key_list: with open(pub_key, 'r') as f: key = JsonWebKey.import_key(f.read(), {'kty': 'RSA'}) update_data = { 'kid': get_timestamp_from_name(pub_key), 'alg': 'RS256', 'kty': 'RSA', 'use': 'sig,' } key.update(update_data) jwt_key_res['keys'].append(key) return jsonify(jwt_key_res)
def api(): req = flask.request access_token = req.headers.get('Authorization', None) if not access_token: return 'Authentication required', 401 # TODO: Validate access-token access_token_parts = access_token.split(' ') if access_token_parts[0].lower() != 'bearer' or len(access_token_parts) != 2: return 'Invalid authorization', 401 access_token = access_token_parts[1] log.info("API: Access token: '{}'".format(access_token)) unverified_access_token_json = jose_jwt.get_unverified_claims(access_token) log.info('API: Unverified claims {}'.format(unverified_access_token_json)) # TODO: Validate that we have an 'iss' claim and that its one we trust token_pub_jwk_json = token_get_jwk(access_token) token_pub_jwk = JsonWebKey.import_key(token_pub_jwk_json) access_token_json = jwt.decode(access_token, token_pub_jwk) scope = access_token_json['scope'] log.info("API: Scope '{}'".format(scope)) if base_url+'/api' in scope.split(' '): api_response = { 'access token scope': access_token_json['scope'], 'info': 'the access token allow access to the api' } return flask.Response(json.dumps(api_response), mimetype='application/json') else: # https://tools.ietf.org/html/rfc6750#section-3 headers = {'WWW-Authenticate': ['Bearer realm='+base_url, 'error=insufficient_scope', 'scope='+base_url+'/api']} return flask.Response(headers=headers), 403
import glob import json from authlib.jose import JsonWebKey def get_timestamp_from_name(key_name): start = key_name.rfind('/') end = key_name.rfind('.key.pub') return key_name[start + 1:end] jwt_key_res = {'keys': []} pub_key_list = glob.glob('*.key.pub') for pub_key in pub_key_list: with open(pub_key, 'r') as f: key = JsonWebKey.import_key(f.read(), {'kty': 'RSA'}) update_data = { 'kid': get_timestamp_from_name(pub_key), 'alg': 'RS256', 'kty': 'RSA', 'use': 'sig,' } key.update(update_data) jwt_key_res['keys'].append(key) print(json.dumps(jwt_key_res, indent=4))
'callback': 'onSubmitCallback', 'size': 'invisible' } # SSL cert SSL_CERT_PATH = os.path.join("instance", "ssl_cert.pem") SSL_KEY_PATH = os.path.join("instance", "ssl_key.pem") # JWK & OIDC JWT RS256_JWK_PUBLIC_PATH = os.path.join("instance", "rs256_jwk.key.pub") RS256_JWK_PRIVATE_PATH = os.path.join("instance", "rs256_jwk.key") JWK_PUBLIC_CONFIG = {} with open(RS256_JWK_PUBLIC_PATH, 'rb') as f: key_data = f.read() key = JsonWebKey.import_key(key_data, options={'kty': 'RSA'}) key_info = key.as_dict() key_info.update({'use': 'sig', 'alg': 'RS256', 'kid': 'the-constant-one'}) JWK_PUBLIC_CONFIG = {"keys": [key_info]} OIDC_JWT_CONFIG = {} with open(RS256_JWK_PRIVATE_PATH, 'rb') as f: key_data = f.read() key = JsonWebKey.import_key(key_data, options={'kty': 'RSA'}) key_info = key.as_dict() key_info.update({'use': 'sig', 'alg': 'RS256', 'kid': 'the-constant-one'}) OIDC_JWT_CONFIG = { 'key': key_info, 'alg': 'RS256', 'iss': 'https://donelogin.ai', 'exp': 3600,
async def get_protected(request: Request): try: access_token = request.headers['authorization'] dpop = request.headers['dpop'] except Exception: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="No access credentials provided.", ) access_token = access_token.replace('DPoP ', '') # Checks Access Token expirations access_token_payload = token.decode_token_section(access_token, 1) try: assert access_token_payload['exp'] >= int( datetime.timestamp(datetime.now())) except AssertionError: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Expired Access Token.", ) dpop_token_payload = token.decode_token_section(dpop, 1) # Checks the DPoP token url and method try: assert 'http://127.0.0.1:8002/protected/data.ttl' == dpop_token_payload[ 'htu'] except AssertionError: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Incorrect request path.", ) try: assert 'GET' == dpop_token_payload['htm'] except AssertionError: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Incorrect request method.", ) # Checks DPoP signature against Access Token dpop_public_key = dpop_token_payload['cnf']['jwk'] try: claims = jwt.decode(dpop, dpop_public_key) claims.validate() except Exception: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Invalid DPoP Signature.", ) dpop_public_key = JsonWebKey.import_key(dpop_public_key, {'kty': 'EC'}) dpop_public_key_thumbprint = dpop_public_key.thumbprint() try: assert dpop_public_key_thumbprint == access_token_payload['cnf']['jwk'] except AssertionError: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Token thumbprints do not match.", ) webid_res = requests.get(access_token_payload['webid']) try: assert webid_res.status_code == 200 except AssertionError: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Unable to access WebID document.", ) # Checks issuer webid_iss = agent.get_webid_idp(access_token_payload['webid']) try: assert webid_iss == access_token_payload['iss'] except AssertionError: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Cannot confiem OIDC issuer.", ) idp_config = requests.get(webid_iss + '/.well-known/openid_configuration') idp_config = idp_config.json() idp_public_keys = requests.get(idp_config['jwks_uri']) idp_public_keys = idp_public_keys.json() access_signature_verified = False for public_key in idp_public_keys: try: claims = jwt.decode(access_token, public_key) claims.validate() access_signature_verified = True except Exception: pass if not access_signature_verified: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Unable to verify access token signature", ) # this is where access control comes into play # Must check that webid in the token has been # granted access to the requested resource return {"message": "This is some protected data. Nice."}
auth_res.url # code is provided in the redirect URL to the client callback client_auth_code = urllib.parse.parse_qs( urllib.parse.urlparse(auth_res.url).query)['code'][0] key = ec.generate_private_key(ec.SECP256R1(), backend=crypto_default_backend()) private_key = key.private_bytes(crypto_serialization.Encoding.PEM, crypto_serialization.PrivateFormat.PKCS8, crypto_serialization.NoEncryption()) public_key = key.public_key().public_bytes( crypto_serialization.Encoding.OpenSSH, crypto_serialization.PublicFormat.OpenSSH) CLIENT_PRIVATE_KEY = JsonWebKey.import_key(private_key, {'kty': 'EC'}) CLIENT_PUBLIC_KEY = JsonWebKey.import_key(public_key, {'kty': 'EC'}) dpop_token_header = {"alg": "ES256", "typ": "dpop+jwt"} dpop_token_payload = { "htu": "http://127.0.0.1:8001", "htm": "POST", "jti": uuid.uuid4().__str__(), "cnf": { "jwk": CLIENT_PUBLIC_KEY }, "iat": int(datetime.datetime.timestamp(datetime.datetime.now())) } dpop_token = jwt.encode(dpop_token_header, dpop_token_payload,
import datetime import json from authlib.jose import jwt, jwk, JsonWebKey with open('jwt-key.pub', 'rb') as f: key_data = f.read() obj = jwk.dumps(key_data, kty='RSA') jwks = {'keys': [obj]} with open('jwks.json', 'w') as f: f.write(json.dumps(jwks)) header = {'alg': 'RS256'} with open('jwt-key', 'rb') as f: key_data = f.read() key = JsonWebKey.import_key(key_data, {'kty': 'RSA'}) payload = { 'sub': 'user1', 'groups': ['user'], 'iss': 'https://github.com/MichaelVL/istio-katas', 'aud': '*', 'iat': datetime.datetime.utcnow(), 'exp': datetime.datetime(year=2030, month=1, day=1) } s = jwt.encode(header, payload, key) with open('user1.jwt', 'w') as f: f.write(s.decode("ascii")) payload = { 'sub': 'user2',
def jwks(): with open(app.config["PUBLIC_KEY_PATH"]) as f: public_key_data = f.read() public_key = JsonWebKey.import_key(public_key_data, {"kty": "RSA"}) keyset = KeySet([public_key]) return keyset.as_json()
def test_thumbprint(self): # https://tools.ietf.org/html/rfc7638#section-3.1 data = read_file_path('thumbprint_example.json') key = JsonWebKey.import_key(data) expected = 'NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs' self.assertEqual(key.thumbprint(), expected)
def token(): req = flask.request log_request('GET-TOKEN', req) client_auth = req.headers.get('Authorization') log.info("GET-TOKEN: Client auth: '{}'".format(client_auth)) # TODO: Validate client auth grant_type = req.form.get('grant_type') log.info("GET-TOKEN: Grant type: '{}'".format(grant_type)) if grant_type == 'authorization_code': code = req.form.get('code') redir_uri = req.form.get('redirection_uri') code_verifier = req.form.get('code_verifier') if code not in code_metadata: log.info("GET-TOKEN: Invalid code: '{}'".format(code)) return flask.make_response(flask.render_template('error.html', text='Invalid code'), 403) log.info("GET-TOKEN: Valid code: '{}'".format(code)) session_id = code_metadata[code]['session_id'] client_id = code_metadata[code]['client_id'] nonce = code_metadata[code]['nonce'] del code_metadata[code] # Code can only be used once # TODO: Validate that code is not too old # TODO: Validate redir_uri and grant type matches code # Context comes from session metadata session = sessions[session_id] subject = session['subject'] client_session = get_client_session_by_id(session, client_id) if client_session['code_challenge']: log.info("GET-TOKEN: Challenge '{}', verifier '{}', method '{}'".format(client_session['code_challenge'], code_verifier, client_session['code_challenge_method'])) if client_session['code_challenge_method'] == 'plain' and code_verifier != client_session['code_challenge']: return flask.make_response('error=invalid_grant', 403) elif client_session['code_challenge_method'] == 'S256': digest = hashlib.sha256(code_verifier.encode('ascii')).digest() our_code_challenge = base64.urlsafe_b64encode(digest).decode('ascii')[:-1] log.info("Self-encoded challenge '{}', got challenge '{}'".format(our_code_challenge, client_session['code_challenge'])) if our_code_challenge != client_session['code_challenge']: return flask.make_response('error=invalid_grant', 403) else: return flask.make_response('error=invalid_grant', 403) scope = client_session['scope'] access_token_lifetime = cfg_access_token_lifetime refresh_token_lifetime = cfg_refresh_token_lifetime elif grant_type == 'refresh_token': refresh_token = req.form.get('refresh_token') log.info('GET-TOKEN: Refresh token {}'.format(refresh_token)) # TODO: Validate refresh token # FIXME with open('jwt-key.pub', 'rb') as f: key_data = f.read() pub_key = JsonWebKey.import_key(key_data, {'kty': 'RSA'}) refresh_token_json = jwt.decode(refresh_token, pub_key) # Context comes from refresh token session_id = refresh_token_json['session_id'] subject = refresh_token_json['sub'] scope = refresh_token_json['scope'] client_id = refresh_token_json['client_id'] access_token_lifetime = refresh_token_json['access_token_lifetime'] refresh_token_lifetime = refresh_token_json['refresh_token_lifetime'] nonce = refresh_token_json['nonce'] else: log.error("GET-TOKEN: Invalid grant type: '{}'".format(grant_type)) return 400 # Issue tokens (shared for both 'authorization_code' and 'refresh_token' grants) log.info('GET-TOKEN: Issuing tokens!') access_token = issue_token(subject, audience=[api_base_url, own_base_url+'/userinfo'], claims={ 'token_use': 'access', 'scope': scope}, expiry=datetime.datetime.utcnow()+datetime.timedelta(seconds=access_token_lifetime)) refresh_token = issue_token(subject, audience=own_base_url+'/token', claims={ 'client_id': client_id, 'session_id': session_id, 'access_token_lifetime': access_token_lifetime, 'refresh_token_lifetime' : refresh_token_lifetime, 'nonce': nonce, 'token_use': 'refresh', 'scope': scope}, expiry=datetime.datetime.utcnow()+datetime.timedelta(seconds=refresh_token_lifetime)) response = {'access_token': access_token, 'expires_in': access_token_lifetime, 'refresh_token': refresh_token, 'token_type': 'Bearer'} if 'openid' in scope: claims = dict() # See https://openid.net/specs/openid-connect-basic-1_0.html#StandardClaims for what claims to include in access token if 'profile' in scope: claims['name'] = 'Name of user {}'.format(subject.capitalize()) claims['preferred_username'] = subject.capitalize() if nonce: claims['nonce'] = nonce response['id_token'] = issue_token(subject, [client_id, own_base_url], claims, datetime.datetime.utcnow()+datetime.timedelta(minutes=60)) return flask.Response(json.dumps(response), mimetype='application/json')
repo_mirror_api = RepoMirrorAPI( app.config, app.config["SERVER_HOSTNAME"], app.config["HTTPCLIENT"], instance_keys=instance_keys, ) tuf_metadata_api = TUFMetadataAPI(app, app.config) # Check for a key in config. If none found, generate a new signing key for Docker V2 manifests. _v2_key_path = os.path.join(OVERRIDE_CONFIG_DIRECTORY, DOCKER_V2_SIGNINGKEY_FILENAME) if os.path.exists(_v2_key_path): with open(_v2_key_path) as key_file: docker_v2_signing_key = JsonWebKey.import_key(key_file.read()) else: docker_v2_signing_key = JsonWebKey.generate_key("RSA", 2048, is_private=True) # Configure the database. if app.config.get("DATABASE_SECRET_KEY") is None and app.config.get( "SETUP_COMPLETE", False): raise Exception( "Missing DATABASE_SECRET_KEY in config; did you perhaps forget to add it?" ) database.configure(app.config) model.config.app_config = app.config
def google_response(login): google_client_id = 'aaa' key = GOOGLE_PRIVATE_KEY cert = GOOGLE_CERT user_claims = { "picture": "http://avatar", "name": "monalisa", } access_token = "b" payload = { "iss": "https://accounts.google.com", "azp": google_client_id, "aud": "1234987819200.apps.googleusercontent.com", "sub": login + "_id", "at_hash": create_half_hash(access_token, "RS256").decode("ascii"), "hd": "example.com", "email": login, "email_verified": "true", "iat": int(time.time()), "exp": int(time.time()) + 100, "nonce": "0394852-3190485-2490358", **user_claims, } header = {"alg": "RS256", "kid": "1"} id_token = jwt.encode(header, payload, key).decode('ascii') response = { '/token': { 'body': { 'access_token': access_token, "id_token": id_token } }, '/oauth2/v3/certs': { "body": { "keys": [{ "kid": "1", **JsonWebKey.import_key(cert, { "kty": "RSA" }).as_dict(), }] } }, '/.well-known/openid-configuration': { "body": { "issuer": "https://accounts.google.com", "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth", # noqa "device_authorization_endpoint": "https://oauth2.googleapis.com/device/code", # noqa "token_endpoint": "https://oauth2.googleapis.com/token", "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo", "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs", } }, } return "test_google", response
async def json_web_key_set(): key: RSAKey = JsonWebKey.import_key( settings.jwt_private_key.get_secret_value()) key_set = KeySet(keys=[key]) return key_set.as_dict()
jwt_key = os.getenv('JWT_KEY', 'jwt-key') app_port = int(os.getenv('APP_PORT', '5001')) own_base_url = os.getenv('APP_BASE_URL', 'http://127.0.0.1:5001') api_base_url = os.getenv('API_BASE_URL', 'http://127.0.0.1:5002/api') SESSION_COOKIE_NAME='session' cfg_access_token_lifetime = int(os.getenv('ACCESS_TOKEN_LIFETIME', '1200')) cfg_refresh_token_lifetime = int(os.getenv('REFRESH_TOKEN_LIFETIME', '3600')) logging.basicConfig() log = logging.getLogger('oauth2-server') log.setLevel(logging.DEBUG) with open(jwt_key, 'rb') as f: key_data = f.read() signing_key = JsonWebKey.import_key(key_data, {'kty': 'RSA'}) with open(jwt_key+'.pub', 'rb') as f: key_data = f.read() signing_key_pub = JsonWebKey.import_key(key_data, {'kty': 'RSA'}) signing_key_pub['kid'] = signing_key_pub.thumbprint() def get_session_by_subject(sub): for session_id in sessions.keys(): if sessions[session_id]['subject'] == sub: return session_id return None def get_client_session_by_id(session, client_id): for cs in session['client_sessions']: if cs['client_id']==client_id:
def callback(): req = flask.request code = req.args.get('code') state = req.args.get('state') # Check state is valid for an outstanding request if state not in outstanding_requests: log.error('State not valid: {}'.format(state)) req_out = outstanding_requests[state] session_id = req_out['session_id'] nonce = req_out['nonce'] del outstanding_requests[state] log.info('Found outstanding request: {} for state {}'.format( req_out, state)) # TODO: Check callback against outstanding requests (e.g. against Cross-Site Request Forgery) log.info("Got callback with code {}, state {}".format(code, state)) if not code: log.error('Received no code, deleting session: {}'.format(session_id)) resp = flask.make_response(flask.redirect(own_url, code=303)) resp.set_cookie(SESSION_COOKIE_NAME, '', samesite='Lax', httponly=True, expires=0) del sessions[session_id] return resp data = { 'code': code, 'grant_type': 'authorization_code', 'redirect_uri': redirect_uri } headers = { 'Authorization': 'Basic ' + encode_client_creds(client_id, client_secret), 'Content-type': 'application/x-www-form-urlencoded' } log.info("Getting token from url: '{}'".format(oauth2_token_url)) response = requests.post(oauth2_token_url, data=data, headers=headers) log_response('CALLBACK', response) if response.status_code != 200: return 'Failed with status {}: {}'.format(response.status_code, response.text) response_json = response.json() for token_type in ['id_token', 'access_token', 'refresh_token']: if token_type in response_json: log.info("Got {} token '{}'".format(token_type, response_json[token_type])) id_token = response_json['id_token'] access_token = response_json['access_token'] refresh_token = response_json['refresh_token'] token_pub_jwk_json = token_get_jwk(id_token) token_pub_jwk = JsonWebKey.import_key(token_pub_jwk_json) claims = jwt.decode(id_token, token_pub_jwk) if nonce and ('nonce' not in claims or claims['nonce'] != nonce): log.error('Nonce mismatch, expected {}, got claims {}'.format(nonce), claims) session_id = req_out['session_id'] session = sessions[session_id] scope = session['scope'] session = { 'id_token': id_token, 'id_token_claims': claims, 'access_token': access_token, 'refresh_token': refresh_token, 'scope': scope } sessions[session_id] = session resp = flask.make_response(flask.redirect(own_url, code=303)) resp.set_cookie(SESSION_COOKIE_NAME, session_id, samesite='Lax', httponly=True) return resp