Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support JWT exp offset as a config parameter and add instead of subtract #39

Merged
merged 2 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 30 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ e.g. `https://hostname/services/oauth2/token`.

The OAuth 2.0 token endpoint URL can be specified either as a
[configuration parameter](#token_url) or using the
`{optional-prefix}SF_TOKEN_URL` environment variable.
`{auth_env_prefix}SF_TOKEN_URL` environment variable.

#### OAuth 2.0 Username-Password Flow

Expand All @@ -780,7 +780,7 @@ the following parameters are required.
The `grant_type` for the OAuth 2.0 Username-Password Flow _must_ be set to
`password` (case-sensitive).

The grant type can also be specified using the `{optional-prefix}SF_GRANT_TYPE`
The grant type can also be specified using the `{auth_env_prefix}SF_GRANT_TYPE`
environment variable.

##### `client_id`
Expand All @@ -793,7 +793,7 @@ This parameter specifies the **consumer key** of the connected app. To access
this value, navigate to "Manage Consumer Details" when viewing the Connected App
details.

The client ID can also be specified using the `{optional-prefix}SF_CLIENT_ID`
The client ID can also be specified using the `{auth_env_prefix}SF_CLIENT_ID`
environment variable.

##### `client_secret`
Expand All @@ -807,7 +807,7 @@ this value, navigate to "Manage Consumer Details" when viewing the Connected App
details.

The client secret can also be specified using the
`{optional-prefix}SF_CLIENT_SECRET` environment variable.
`{auth_env_prefix}SF_CLIENT_SECRET` environment variable.

##### `username`

Expand All @@ -818,7 +818,7 @@ The client secret can also be specified using the
This parameter specifies the **username** that the connected app will
impersonate/imitate for authentication and authorization purposes.

The username can also be specified using the `{optional-prefix}SF_USERNAME`
The username can also be specified using the `{auth_env_prefix}SF_USERNAME`
environment variable.

##### `password`
Expand All @@ -830,7 +830,7 @@ environment variable.
This parameter specifies the **password** of the user that the connected app
will impersonate/imitate for authentication and authorization purposes.

The password can also be specified using the `{optional-prefix}SF_PASSWORD`
The password can also be specified using the `{auth_env_prefix}SF_PASSWORD`
environment variable.

**NOTE:** As noted in [the OAuth 2.0 Username-Password flow documentation](https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_username_password_flow.htm&type=5),
Expand Down Expand Up @@ -876,7 +876,7 @@ the following parameters are required.
The `grant_type` for the OAuth 2.0 JWT Bearer Flow _must_ be set to
`urn:ietf:params:oauth:grant-type:jwt-bearer` (case-sensitive).

The grant type can also be specified using the `{optional-prefix}SF_GRANT_TYPE`
The grant type can also be specified using the `{auth_env_prefix}SF_GRANT_TYPE`
environment variable.

##### `client_id`
Expand All @@ -889,7 +889,7 @@ This parameter specifies the **client_id** generated and assigned to the
connected app when it is saved after
[registering the X509 certificate](https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_jwt_flow.htm&type=5)

The client ID can also be specified using the `{optional-prefix}SF_CLIENT_ID`
The client ID can also be specified using the `{auth_env_prefix}SF_CLIENT_ID`
environment variable.

##### `private_key`
Expand All @@ -904,7 +904,7 @@ key that is associated with the X509 certificate that is
with the connected app. The private key is used to sign the JWT.

The private key file path can also be specified using the
`{optional-prefix}SF_PRIVATE_KEY` environment variable.
`{auth_env_prefix}SF_PRIVATE_KEY` environment variable.

##### `subject`

Expand All @@ -917,7 +917,7 @@ Set for the JWT. Per [the documentation](https://help.salesforce.com/s/articleVi
this should be set to the user's username when accessing an Experience Cloud
site.

The subject can also be specified using the `{optional-prefix}SF_SUBJECT`
The subject can also be specified using the `{auth_env_prefix}SF_SUBJECT`
environment variable.

##### `audience`
Expand All @@ -930,9 +930,22 @@ This parameter specifies the value used for the `aud` claim in the JSON Claims
Set for the JWT. Per [the documentation](https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_jwt_flow.htm&type=5),
this should be set to the authorization server's URL.

The audience can also be specified using the `{optional-prefix}SF_AUDIENCE`
The audience can also be specified using the `{auth_env_prefix}SF_AUDIENCE`
environment variable.

##### `expiration_offset`

| Description | Valid Values | Required | Default |
| --- | --- | --- | --- |
| An offset duration (in _minutes_) to use when calculating the JWT `exp` claim | integer | N | 5 |

This value of this parameter is added to the current time and the result is used
as the value of the `exp` claim in the JSON Claims Set for the JWT. The value
_must_ be a positive integer.
jbeveland27 marked this conversation as resolved.
Show resolved Hide resolved

The expiration offset can also be specified using the
`{auth_env_prefix}SF_EXPIRATION_OFFSET` environment variable.

##### Example

Below is an example OAuth 2.0 JWT Bearer Flow configuration in the
Expand Down Expand Up @@ -1727,7 +1740,7 @@ The following configuration parameters are supported.

This parameter specifies the hostname or IP address of the Redis server.

The host can also be specified using the `{optional-prefix}REDIS_HOST`
The host can also be specified using the `{auth_env_prefix}REDIS_HOST`
environment variable.

##### `port`
Expand All @@ -1738,7 +1751,7 @@ environment variable.

This parameter specifies the port to connect to on the Redis server.

The port can also be specified using the `{optional-prefix}REDIS_PORT`
The port can also be specified using the `{auth_env_prefix}REDIS_PORT`
environment variable.

##### `db_number`
Expand All @@ -1750,7 +1763,7 @@ environment variable.
This parameter specifies the database number to connect to on the Redis server.

The database number can also be specified using the
`{optional-prefix}REDIS_DB_NUMBER` environment variable.
`{auth_env_prefix}REDIS_DB_NUMBER` environment variable.

##### `ssl`

Expand All @@ -1761,7 +1774,7 @@ The database number can also be specified using the
This parameter specifies whether or not to use an SSL connection to connect to
the Redis server.

The SSL flag can also be specified using the `{optional-prefix}REDIS_SSL`
The SSL flag can also be specified using the `{auth_env_prefix}REDIS_SSL`
environment variable.

##### `password`
Expand All @@ -1772,7 +1785,7 @@ environment variable.

This parameter specifies the password to use to connect to the Redis server.

The password can also be specified using the `{optional-prefix}REDIS_PASSWORD`
The password can also be specified using the `{auth_env_prefix}REDIS_PASSWORD`
environment variable.

##### `expire_days`
Expand All @@ -1784,7 +1797,7 @@ environment variable.
This parameter specifies the expiration time to use when putting any entry into
the cache. The time is specified in days.

The expiry can also be specified using the `{optional-prefix}REDIS_EXPIRE_DAYS`
The expiry can also be specified using the `{auth_env_prefix}REDIS_EXPIRE_DAYS`
environment variable.

##### Example
Expand Down
23 changes: 22 additions & 1 deletion src/newrelic_logging/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
SF_SUBJECT = 'SF_SUBJECT'
SF_AUDIENCE = 'SF_AUDIENCE'
SF_TOKEN_URL = 'SF_TOKEN_URL'
SF_EXPIRATION_OFFSET = 'SF_EXPIRATION_OFFSET'
DEFAULT_EXPIRATION_OFFSET = 5


class Authenticator:
Expand Down Expand Up @@ -104,7 +106,9 @@ def authenticate_with_jwt(self, session: Session) -> None:
client_id = self.auth_data['client_id']
subject = self.auth_data['subject']
audience = self.auth_data['audience']
exp = int((datetime.utcnow() - timedelta(minutes=5)).timestamp())
exp = int((
datetime.utcnow() + timedelta(minutes=self.auth_data['exp_offset']),
).timestamp())

with open(private_key_file, 'r') as f:
try:
Expand Down Expand Up @@ -231,6 +235,12 @@ def validate_jwt_config(auth: dict) -> dict:
if not auth['audience']:
raise ConfigException('audience', 'missing JWT audience')

if auth['exp_offset'] <= 0:
raise ConfigException(
'exp_offset',
'JWT expiration offset must be greater than zero',
)

return auth


Expand All @@ -254,12 +264,19 @@ def make_auth_from_config(auth: Config) -> dict:
})

if grant_type == 'urn:ietf:params:oauth:grant-type:jwt-bearer':
exp_offset = auth.get(
'expiration_offset',
env_var_name=SF_EXPIRATION_OFFSET,
)

return validate_jwt_config({
'grant_type': grant_type,
'client_id': auth.get('client_id', env_var_name=SF_CLIENT_ID),
'private_key': auth.get('private_key', env_var_name=SF_PRIVATE_KEY),
'subject': auth.get('subject', env_var_name=SF_SUBJECT),
'audience': auth.get('audience', env_var_name=SF_AUDIENCE),
'exp_offset': int(exp_offset) if not exp_offset is None \
else DEFAULT_EXPIRATION_OFFSET,
})

raise Exception(f'Wrong or missing grant_type')
Expand All @@ -278,12 +295,16 @@ def make_auth_from_env(config: Config) -> dict:
})

if grant_type == 'urn:ietf:params:oauth:grant-type:jwt-bearer':
exp_offset = config.getenv(SF_EXPIRATION_OFFSET)

return validate_jwt_config({
'grant_type': grant_type,
'client_id': config.getenv(SF_CLIENT_ID),
'private_key': config.getenv(SF_PRIVATE_KEY),
'subject': config.getenv(SF_SUBJECT),
'audience': config.getenv(SF_AUDIENCE),
'exp_offset': int(exp_offset) if not exp_offset is None \
else DEFAULT_EXPIRATION_OFFSET,
})

raise Exception(f'Wrong or missing grant_type')
Loading