Exemplo n.º 1
0
    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"])))
Exemplo n.º 2
0
    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'
Exemplo n.º 3
0
    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))
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
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
Exemplo n.º 7
0
    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)
Exemplo n.º 8
0
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
Exemplo n.º 9
0
Arquivo: schema1.py Projeto: syed/quay
    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()
Exemplo n.º 10
0
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'}]}
Exemplo n.º 11
0
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
Exemplo n.º 12
0
    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
Exemplo n.º 13
0
    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"]
            ]))
Exemplo n.º 14
0
    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)
Exemplo n.º 15
0
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)))
Exemplo n.º 16
0
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)
Exemplo n.º 17
0
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
Exemplo n.º 18
0
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))
Exemplo n.º 19
0
    '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,
Exemplo n.º 20
0
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."}
Exemplo n.º 21
0
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,
Exemplo n.º 22
0
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',
Exemplo n.º 23
0
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()
Exemplo n.º 24
0
 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')
Exemplo n.º 26
0
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
Exemplo n.º 27
0
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
Exemplo n.º 28
0
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:
Exemplo n.º 30
0
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