def test_rsa_key_from_jwk() -> None: key = jwt_utils.rsa_key_from_jwk(json.dumps(RSA_JWK)) assert key assert isinstance(key, str) # The PEM keys are not equal, and by more than just the header and footer ("BEGIN RSA # PRIVATE KEY" vs "BEGIN PRIVATE KEY"). There might be some more metadata in there that # is not relevant. However below we assert the generated tokens are identical. # assert key == RS256_KEY.lstrip() # Ensure we can use the key to create a token claims = {"iss": "me"} token_from_jwk = jwt_utils.encode(claims, key, algorithm="RS256") token = jwt_utils.encode(claims, RS256_KEY, algorithm="RS256") assert token_from_jwk == token
def _get_authorization_header( credentials: AppConnectCredentials, expiry_sec: Optional[int] = None) -> Mapping[str, str]: """Creates a JWT (javascript web token) for use with app store connect API All requests to app store connect require an "Authorization" header build as below. Note: The maximum allowed expiry time is 20 minutes. The default is somewhat shorter than that to avoid running into the limit. :return: the Bearer auth token to be added as the "Authorization" header """ if expiry_sec is None: expiry_sec = 60 * 10 # default to 10 mins with sentry_sdk.start_span( op="jwt", description="Generating AppStoreConnect JWT token"): token = jwt.encode( { "iss": credentials.issuer_id, "exp": int(time.time()) + expiry_sec, "aud": "appstoreconnect-v1", }, credentials.key, algorithm="ES256", headers={ "kid": credentials.key_id, "alg": "ES256", "typ": "JWT" }, ) return jwt.authorization_header(token)
def test_encode_rs256() -> None: headers = { "alg": "RS256", "typ": "JWT", } claims = { "iss": "me", } encoded_hs256 = jwt_utils.encode(claims, "secret", headers=headers) encoded_rs256 = jwt_utils.encode(claims, RS256_KEY, headers=headers, algorithm="RS256") assert encoded_rs256.count(".") == 2 assert encoded_rs256 != encoded_hs256
def get_token(self, issuer, uri, method): now = int(time()) payload = { "iss": issuer, "iat": now, "exp": now + 60 * 60, "qsh": get_query_hash(uri, method), "aud": issuer, } return jwt.encode(payload, self.secret, algorithm="HS256")
def request(self, method, path, data=None, params=None, **kwargs): jwt_payload = { "iss": BITBUCKET_KEY, "iat": datetime.datetime.utcnow(), "exp": datetime.datetime.utcnow() + datetime.timedelta(seconds=5 * 60), "qsh": get_query_hash(path, method.upper(), params), "sub": self.subject, } encoded_jwt = jwt.encode(jwt_payload, self.shared_secret) headers = jwt.authorization_header(encoded_jwt, scheme="JWT") return self._request(method, path, data=data, params=params, headers=headers, **kwargs)
def test_decode(token: str) -> None: claims = jwt_utils.decode(token, "secret") assert claims == {"iss": "me"} for key, value in claims.items(): assert isinstance(key, str) assert isinstance(value, str) claims["aud"] = "you" token = jwt_utils.encode(claims, "secret") with pytest.raises(pyjwt.exceptions.InvalidAudienceError): jwt_utils.decode(token, "secret")
def get_jwt(self): exp = datetime.datetime.utcnow() + datetime.timedelta(minutes=10) exp = calendar.timegm(exp.timetuple()) # Generate the JWT payload = { # issued at time "iat": int(time.time()), # JWT expiration time (10 minute maximum) "exp": exp, # Integration's GitHub identifier "iss": options.get("github.integration-app-id"), } return jwt.encode(payload, options.get("github.integration-private-key"), algorithm="RS256")
def get_jwt(github_id: str | None = None, github_private_key: str | None = None) -> str: if github_id is None: github_id = options.get("github-app.id") if github_private_key is None: github_private_key = options.get("github-app.private-key") exp_ = datetime.datetime.utcnow() + datetime.timedelta(minutes=10) exp = calendar.timegm(exp_.timetuple()) # Generate the JWT payload = { # issued at time "iat": int(time.time()), # JWT expiration time (10 minute maximum) "exp": exp, # Integration's GitHub identifier "iss": github_id, } return jwt.encode(payload, github_private_key, algorithm="RS256")
def test_encode(token: str) -> None: headers = { "alg": "HS256", "typ": "JWT", } claims = { "iss": "me", } key = "secret" encoded = jwt_utils.encode(claims, key, headers=headers) assert isinstance(encoded, str) assert encoded.count(".") == 2 assert encoded == token decoded_claims = jwt_utils.decode(encoded, key) assert decoded_claims == claims
def test_decode_audience() -> None: payload = { "iss": "me", "aud": "you", } token = jwt_utils.encode(payload, "secret") with pytest.raises(pyjwt.exceptions.InvalidAudienceError): jwt_utils.decode(token, "secret") claims = jwt_utils.decode(token, "secret", audience="you") assert claims == payload with pytest.raises(pyjwt.exceptions.InvalidAudienceError): jwt_utils.decode(token, "secret", audience="wrong") claims = jwt_utils.decode(token, "secret", audience=False) assert claims == payload
def request_hook(self, method, path, data, params, **kwargs): """ Used by Jira Client to apply the jira-cloud authentication """ # handle params that are already part of the path url_params = dict(parse_qs(urlsplit(path).query)) url_params.update(params or {}) path = path.split("?")[0] jwt_payload = { "iss": JIRA_KEY, "iat": datetime.datetime.utcnow(), "exp": datetime.datetime.utcnow() + datetime.timedelta(seconds=5 * 60), "qsh": get_query_hash(path, method.upper(), url_params), } encoded_jwt = jwt.encode(jwt_payload, self.shared_secret) params = dict(jwt=encoded_jwt, **(url_params or {})) request_spec = kwargs.copy() request_spec.update(dict(method=method, path=path, data=data, params=params)) return request_spec
def create_issue_webhook(self, external_id, secret, credentials): auth = OAuth1( client_key=credentials["consumer_key"], rsa_key=credentials["private_key"], resource_owner_key=credentials["access_token"], resource_owner_secret=credentials["access_token_secret"], signature_method=SIGNATURE_RSA, signature_type="auth_header", ) # Create a JWT token that we can add to the webhook URL # so we can locate the matching integration later. token = jwt.encode({"id": external_id}, secret) path = reverse("sentry-extensions-jiraserver-issue-updated", kwargs={"token": token}) data = { "name": "Sentry Issue Sync", "url": absolute_uri(path), "events": ["jira:issue_created", "jira:issue_updated"], } return self.post("/rest/webhooks/1.0/webhook", auth=auth, data=data)