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

Revisit request authentication #472

Closed
tgeoghegan opened this issue Sep 6, 2022 · 11 comments
Closed

Revisit request authentication #472

tgeoghegan opened this issue Sep 6, 2022 · 11 comments
Assignees
Labels
byohelper needed for BYOHelper and self-service task provisioning (#1486)

Comments

@tgeoghegan
Copy link
Contributor

draft-ietf-ppm-dap-02 no longer requires the DAP-Auth-Token:

ietf-wg-ppm/draft-ietf-ppm-dap#293
ietf-wg-ppm/draft-ietf-ppm-dap#305

DAP-Auth-Token isn't forbidden by the new text, so we could leave that support in for backward compatibility with other DAP implementations, but we should look into implementing more secure authentication schemes for Divvi Up, perhaps leaning on managed identity offerings.

@tgeoghegan tgeoghegan added this to the draft-ietf-ppm-dap-02 milestone Sep 6, 2022
@tgeoghegan tgeoghegan self-assigned this Sep 6, 2022
@tgeoghegan
Copy link
Contributor Author

I think we will not make changes here as part of our DAP-02 implementation, since this touches on some bigger questions about subscriber management and the Divvi Up control plane. Removing this from the draft-ietf-ppm-dap-02 milestone.

@tgeoghegan tgeoghegan removed this from the draft-ietf-ppm-dap-02 milestone Nov 29, 2022
@tgeoghegan tgeoghegan added this to the Production readiness milestone Jan 24, 2023
@tgeoghegan
Copy link
Contributor Author

The POR for Divvi Up authentication is:

  • Subscribers authenticate to the control plane (to manage tasks and such) via Auth0
  • When the data plane creates a task, it generates a per-task bearer token which is supplied to the subscriber. The peer aggregator subsequently uses that bearer token to authenticate to the DU aggregator

I think all that has already been implemented, so I don't believe there's anything else to do here except to make sure all the task provisioning flows work, which is captured by #44.

@tgeoghegan
Copy link
Contributor Author

In the future, we might implement more sophisticated authz -- for instance, issue tokens that are only permitted to exercise either the aggregation job or collection job sub-protocols -- but I think we have everything we need in place for the initial production deployment(s) of Divvi Up.

@branlwyd @divergentdave Is there anything else you think we should do w.r.t. request authentication? At most I think we might want to put a version into the bearer tokens we generate, which might make it easier to migrate to more sophisticated schemes in the future, but I'm not sure that's necessary.

@branlwyd
Copy link
Member

I think we are fine with a bearer token. I wouldn't object to adding a version, but I don't think it's strictly necessary -- presumably any authentication token will have enough unguessable material that it is overwhelmingly unlikely that a valid auth token for one scheme is also valid in another scheme. (or, to think of it another way, a token from one scheme being valid in another scheme had better not be any more likely than a random string of bytes being valid in that scheme) OTOH, including a version would make the hypothetical code to parse multiple versions of tokens much simpler. A nice-to-have but not a requirement.

@tgeoghegan
Copy link
Contributor Author

I agree that token confusion is unlikely, but I think a version would be helpful when we move to a different token scheme (e.g. maybe we move to Macaroons as we've discussed) so that we can easily detect obsolete bearer tokens and force subscribers to refresh. But then again, since we store the bearer tokens in the Janus DB, we know what version or scheme they were issued under (we might eventually add a version column to task_aggregator_auth_tokens but its absence implies version 0) so I think we don't need to encode anything into the token clients hold.

@divergentdave
Copy link
Contributor

We should probably use a different header name when we change to a different authentication scheme, be it Authorization or something else. If that's the case, then I think it's fine to continue using DAP-Auth-Token with no version number structure in its value.

@tgeoghegan
Copy link
Contributor Author

Oh, I'd forgotten we are still using DAP-Auth-Token. I got confused because the aggregator API already uses proper Authorization headers.

Now I'm more concerned, and wonder if we should switch to bearer tokens now. There's nothing wrong as such with DAP-Auth-Token, but it's weird and different from the much more widely used Authorization: Bearer for no good reason. And it'd be better to have the auth mechanism be consistent across the control plane API, aggregator API and data plane API.

Changing the opaque token format is something we most likely can do without changes to clients, but it'd be a hassle to switch headers once we've got any number of deployed peer aggregators, or worse, clients.

On the other hand, which header we use doesn't really matter and is arguably an aesthetic issue, so it might not be worth changing.

@tgeoghegan
Copy link
Contributor Author

Following discussion in Slack: we're going to migrate to using proper bearer tokens. The primary motivation here is consistency across our API surfaces. To ease migration (for instance with Daphne), we'll examine both the DAP-Auth-Token and Authorization headers for some period and then cut over to exclusively honoring bearer tokens. #1300 tracks deprecating DAP-Auth-Token.

@tgeoghegan
Copy link
Contributor Author

tgeoghegan commented Apr 26, 2023

This ends up being kind of tricky. It's not too hard to teach janus_aggregator to accept either Authorization: Bearer <token> or DAP-Auth-Token: <token>, preferring the former, when handling aggregation job, collection or aggregate share requests. However we also want Janus' various components to start using proper bearer tokens when they are acting as an HTTP client, if the HTTP server expects that. But a Janus leader might be talking to helper run by an older Janus that only accepts DAP-Auth-Token, or by Daphne. I think that means we have to add a field to the Task structure (and a corresponding column to the tasks table) to indicate what authentication mode the helper expects. In turn, that means that the aggregator API and I think divviup-api need a way to indicate what authentication mode the helper uses for a task.

To keep review manageable, I'm going to stage this in multiple changes:

tgeoghegan added a commit that referenced this issue Apr 27, 2023
When acting as an HTTP server (i.e., helpers handling aggregation job or
aggregate share messages, or leaders handling collection requests),
Janus will now check for the authentication token in the more standard
`Authorization: Bearer <token>` format defined in RFC 6750, as it
already does in the aggregator API. If that header is absent, it falls
back to checking `DAP-Auth-Token` for compatibility with existing peers.
Nothing changes about the format or handling of the authentication token
itself, which remains an opaque blob o' bytes.

Part of #472
tgeoghegan added a commit that referenced this issue Apr 27, 2023
When acting as an HTTP server (i.e., helpers handling aggregation job or
aggregate share messages, or leaders handling collection requests),
Janus will now check for the authentication token in the more standard
`Authorization: Bearer <token>` format defined in RFC 6750, as it
already does in the aggregator API. If that header is absent, it falls
back to checking `DAP-Auth-Token` for compatibility with existing peers.
Nothing changes about the format or handling of the authentication token
itself, which remains an opaque blob o' bytes.

Part of #472
tgeoghegan added a commit that referenced this issue Apr 27, 2023
When acting as an HTTP server (i.e., helpers handling aggregation job or
aggregate share messages, or leaders handling collection requests),
Janus will now check for the authentication token in the more standard
`Authorization: Bearer <token>` format defined in RFC 6750, as it
already does in the aggregator API. If that header is absent, it falls
back to checking `DAP-Auth-Token` for compatibility with existing peers.
Nothing changes about the format or handling of the authentication token
itself, which remains an opaque blob o' bytes.

Part of #472
tgeoghegan added a commit that referenced this issue Apr 27, 2023
When acting as an HTTP server (i.e., helpers handling aggregation job or
aggregate share messages, or leaders handling collection requests),
Janus will now check for the authentication token in the more standard
`Authorization: Bearer <token>` format defined in RFC 6750, as it
already does in the aggregator API. If that header is absent, it falls
back to checking `DAP-Auth-Token` for compatibility with existing peers.
Nothing changes about the format or handling of the authentication token
itself, which remains an opaque blob o' bytes.

Part of #472
tgeoghegan added a commit that referenced this issue May 19, 2023
Previously, we taught Janus to accept authentication tokens presented in
HTTP requests as either `DAP-Auth-Token: <token>` or `Authorization:
Bearer <token>`. However, we also want a Janus leader to be able to work
with a DAP helper that expects only one or the other authentication
mode.

The `task_aggregator_auth_tokens` and `task_collector_auth_tokens`
tables now have columns that track the type of auth token, which is
either `DAP_AUTH` or `BEARER`. `send_request_to_helper` will now consult
this value to decide how it should authenticate requests.

We didn't strictly need this column in `task_collector_auth_tokens` as
the leader's collection job handlers will still accept either kind of
token, but it's nice to have consistent handling of these types across
the two tables.

The distinction between DAP-Auth-Token and bearer tokens is plumbed up
from `janus_core` through `janus_aggregator` by combining
`janus_core::task::AuthenticationToken` with
`janus_collector::Authentication` into an enum that distingishes between
the two authentication modes and can produce suitable HTTP header names
and values.

Note that we track authentication mode per-token and not per-task. This
means that it's possible for a task to use two different kinds of
tokens.

A task defined in YAML and provisioned via `janus_cli` may use Bearer
tokens, but this isn't yet possible via `janus_aggregator_api`, which
still assumes it is receiving or generating `DAP-Auth-Token`-form
tokens. The aggregator API for creating tasks will have to change to
accomodate the distinction between token types, and that will arrive in
a later change so that it can be coordinated with `divviup-api`.

Part of #472
tgeoghegan added a commit that referenced this issue May 19, 2023
Previously, we taught Janus to accept authentication tokens presented in
HTTP requests as either `DAP-Auth-Token: <token>` or `Authorization:
Bearer <token>`. However, we also want a Janus leader to be able to work
with a DAP helper that expects only one or the other authentication
mode.

The `task_aggregator_auth_tokens` and `task_collector_auth_tokens`
tables now have columns that track the type of auth token, which is
either `DAP_AUTH` or `BEARER`. `send_request_to_helper` will now consult
this value to decide how it should authenticate requests.

We didn't strictly need this column in `task_collector_auth_tokens` as
the leader's collection job handlers will still accept either kind of
token, but it's nice to have consistent handling of these types across
the two tables.

The distinction between DAP-Auth-Token and bearer tokens is plumbed up
from `janus_core` through `janus_aggregator` by combining
`janus_core::task::AuthenticationToken` with
`janus_collector::Authentication` into an enum that distingishes between
the two authentication modes and can produce suitable HTTP header names
and values.

Note that we track authentication mode per-token and not per-task. This
means that it's possible for a task to use two different kinds of
tokens.

A task defined in YAML and provisioned via `janus_cli` may use Bearer
tokens, but this isn't yet possible via `janus_aggregator_api`, which
still assumes it is receiving or generating `DAP-Auth-Token`-form
tokens. The aggregator API for creating tasks will have to change to
accomodate the distinction between token types, and that will arrive in
a later change so that it can be coordinated with `divviup-api`.

Part of #472
tgeoghegan added a commit that referenced this issue May 22, 2023
Previously, we taught Janus to accept authentication tokens presented in
HTTP requests as either `DAP-Auth-Token: <token>` or `Authorization:
Bearer <token>`. However, we also want a Janus leader to be able to work
with a DAP helper that expects only one or the other authentication
mode.

The `task_aggregator_auth_tokens` and `task_collector_auth_tokens`
tables now have columns that track the type of auth token, which is
either `DAP_AUTH` or `BEARER`. `send_request_to_helper` will now consult
this value to decide how it should authenticate requests.

We didn't strictly need this column in `task_collector_auth_tokens` as
the leader's collection job handlers will still accept either kind of
token, but it's nice to have consistent handling of these types across
the two tables.

The distinction between DAP-Auth-Token and bearer tokens is plumbed up
from `janus_core` through `janus_aggregator` by combining
`janus_core::task::AuthenticationToken` with
`janus_collector::Authentication` into an enum that distingishes between
the two authentication modes and can produce suitable HTTP header names
and values.

Note that we track authentication mode per-token and not per-task. This
means that it's possible for a task to use two different kinds of
tokens.

A task defined in YAML and provisioned via `janus_cli` may use Bearer
tokens, but this isn't yet possible via `janus_aggregator_api`, which
still assumes it is receiving or generating `DAP-Auth-Token`-form
tokens. The aggregator API for creating tasks will have to change to
accomodate the distinction between token types, and that will arrive in
a later change so that it can be coordinated with `divviup-api`.

Part of #472
tgeoghegan added a commit that referenced this issue May 22, 2023
Previously, we taught Janus to accept authentication tokens presented in
HTTP requests as either `DAP-Auth-Token: <token>` or `Authorization:
Bearer <token>`. However, we also want a Janus leader to be able to work
with a DAP helper that expects only one or the other authentication
mode.

The `task_aggregator_auth_tokens` and `task_collector_auth_tokens`
tables now have columns that track the type of auth token, which is
either `DAP_AUTH` or `BEARER`. `send_request_to_helper` will now consult
this value to decide how it should authenticate requests.

We didn't strictly need this column in `task_collector_auth_tokens` as
the leader's collection job handlers will still accept either kind of
token, but it's nice to have consistent handling of these types across
the two tables.

The distinction between DAP-Auth-Token and bearer tokens is plumbed up
from `janus_core` through `janus_aggregator` by combining
`janus_core::task::AuthenticationToken` with
`janus_collector::Authentication` into an enum that distingishes between
the two authentication modes and can produce suitable HTTP header names
and values.

Note that we track authentication mode per-token and not per-task. This
means that it's possible for a task to use two different kinds of
tokens.

A task defined in YAML and provisioned via `janus_cli` may use Bearer
tokens, but this isn't yet possible via `janus_aggregator_api`, which
still assumes it is receiving or generating `DAP-Auth-Token`-form
tokens. The aggregator API for creating tasks will have to change to
accomodate the distinction between token types, and that will arrive in
a later change so that it can be coordinated with `divviup-api`.

Part of #472
@tgeoghegan tgeoghegan added the byohelper needed for BYOHelper and self-service task provisioning (#1486) label Jun 16, 2023
@tgeoghegan
Copy link
Contributor Author

Applying the byohelper label here: the BYOHelper + self-service task provisioning work involves a bunch of tweaks to the aggregator API and divviup-api's usage of it. That provides a great opportunity to switch the default behavior for new task generation from minting DAP-Auth-Token tokens to instead using RFC 6750 bearer tokens.

tgeoghegan added a commit that referenced this issue Jun 29, 2023
Previously, the aggregator API assumed that any aggregator or collector
auth tokens it was handling were `AuthenticationToken::DapAuth`. Janus
must support those for the interop testing API and to work with Daphne,
but generally, we prefer to use more boring `Authorization: Bearer`
tokens. Certainly any tasks provisioned via the aggregator API and
`divviup-api` should use bearer tokens.

With this PR, we augment the `PostTaskReq` and `PostTaskResp` messages
so that they use `AuthenticationToken` to represent aggregator and
collector tokens, meaning that on the wire, a token now looks like:

```
{
  "type": "Bearer",
  "token": "AAAAAAAAA<etc.>"
}
```

Also, when the aggregator API generates either kind of token, it now
generates a bearer token. I'm not aware of any scenarios where we need
to generate `DapAuth` tokens in the aggregator API so there is no
affordance for requesting that kind of token.

See #472
tgeoghegan added a commit that referenced this issue Jun 29, 2023
Previously, the aggregator API assumed that any aggregator or collector
auth tokens it was handling were `AuthenticationToken::DapAuth`. Janus
must support those for the interop testing API and to work with Daphne,
but generally, we prefer to use more boring `Authorization: Bearer`
tokens. Certainly any tasks provisioned via the aggregator API and
`divviup-api` should use bearer tokens.

With this PR, we augment the `PostTaskReq` and `PostTaskResp` messages
so that they use `AuthenticationToken` to represent aggregator and
collector tokens, meaning that on the wire, a token now looks like:

```
{
  "type": "Bearer",
  "token": "AAAAAAAAA<etc.>"
}
```

Also, when the aggregator API generates either kind of token, it now
generates a bearer token. I'm not aware of any scenarios where we need
to generate `DapAuth` tokens in the aggregator API so there is no
affordance for requesting that kind of token.

See #472
tgeoghegan added a commit that referenced this issue Jul 26, 2023
Previously, the aggregator API assumed that any aggregator or collector
auth tokens it was handling were `AuthenticationToken::DapAuth`. Janus
must support those for the interop testing API and to work with Daphne,
but generally, we prefer to use more boring `Authorization: Bearer`
tokens. Certainly any tasks provisioned via the aggregator API and
`divviup-api` should use bearer tokens.

With this PR, we augment the `PostTaskReq` and `PostTaskResp` messages
so that they use `AuthenticationToken` to represent aggregator and
collector tokens, meaning that on the wire, a token now looks like:

```
{
  "type": "Bearer",
  "token": "AAAAAAAAA<etc.>"
}
```

Also, when the aggregator API generates either kind of token, it now
generates a bearer token. I'm not aware of any scenarios where we need
to generate `DapAuth` tokens in the aggregator API so there is no
affordance for requesting that kind of token.

See #472
tgeoghegan added a commit that referenced this issue Jul 31, 2023
Previously, the aggregator API assumed that any aggregator or collector
auth tokens it was handling were `AuthenticationToken::DapAuth`. Janus
must support those for the interop testing API and to work with Daphne,
but generally, we prefer to use more boring `Authorization: Bearer`
tokens. Certainly any tasks provisioned via the aggregator API and
`divviup-api` should use bearer tokens.

With this PR, we augment the `PostTaskReq` and `PostTaskResp` messages
so that they use `AuthenticationToken` to represent aggregator and
collector tokens, meaning that on the wire, a token now looks like:

```
{
  "type": "Bearer",
  "token": "AAAAAAAAA<etc.>"
}
```

Also, when the aggregator API generates either kind of token, it now
generates a bearer token. I'm not aware of any scenarios where we need
to generate `DapAuth` tokens in the aggregator API so there is no
affordance for requesting that kind of token.

See #472
tgeoghegan added a commit that referenced this issue Aug 1, 2023
Previously, the aggregator API assumed that any aggregator or collector
auth tokens it was handling were `AuthenticationToken::DapAuth`. Janus
must support those for the interop testing API and to work with Daphne,
but generally, we prefer to use more boring `Authorization: Bearer`
tokens. Certainly any tasks provisioned via the aggregator API and
`divviup-api` should use bearer tokens.

With this PR, we augment the `PostTaskReq` and `PostTaskResp` messages
so that they use `AuthenticationToken` to represent aggregator and
collector tokens, meaning that on the wire, a token now looks like:

```
{
  "type": "Bearer",
  "token": "AAAAAAAAA<etc.>"
}
```

Also, when the aggregator API generates either kind of token, it now
generates a bearer token. I'm not aware of any scenarios where we need
to generate `DapAuth` tokens in the aggregator API so there is no
affordance for requesting that kind of token.

See #472
tgeoghegan added a commit that referenced this issue Aug 1, 2023
Previously, the aggregator API assumed that any aggregator or collector
auth tokens it was handling were `AuthenticationToken::DapAuth`. Janus
must support those for the interop testing API and to work with Daphne,
but generally, we prefer to use more boring `Authorization: Bearer`
tokens. Certainly any tasks provisioned via the aggregator API and
`divviup-api` should use bearer tokens.

With this PR, we augment the `PostTaskReq` and `PostTaskResp` messages
so that they use `AuthenticationToken` to represent aggregator and
collector tokens, meaning that on the wire, a token now looks like:

```
{
  "type": "Bearer",
  "token": "AAAAAAAAA<etc.>"
}
```

Also, when the aggregator API generates either kind of token, it now
generates a bearer token. I'm not aware of any scenarios where we need
to generate `DapAuth` tokens in the aggregator API so there is no
affordance for requesting that kind of token.

See #472
tgeoghegan added a commit that referenced this issue Aug 1, 2023
Previously, the aggregator API assumed that any aggregator or collector
auth tokens it was handling were `AuthenticationToken::DapAuth`. Janus
must support those for the interop testing API and to work with Daphne,
but generally, we prefer to use more boring `Authorization: Bearer`
tokens. Certainly any tasks provisioned via the aggregator API and
`divviup-api` should use bearer tokens.

With this PR, we augment the `PostTaskReq` and `PostTaskResp` messages
so that they use `AuthenticationToken` to represent aggregator and
collector tokens, meaning that on the wire, a token now looks like:

```
{
  "type": "Bearer",
  "token": "AAAAAAAAA<etc.>"
}
```

Also, when the aggregator API generates either kind of token, it now
generates a bearer token. I'm not aware of any scenarios where we need
to generate `DapAuth` tokens in the aggregator API so there is no
affordance for requesting that kind of token.

See #472
@tgeoghegan
Copy link
Contributor Author

#1548 and the corresponding divviup-api change is the last thing I had in mind here, so closing this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
byohelper needed for BYOHelper and self-service task provisioning (#1486)
Projects
None yet
Development

No branches or pull requests

3 participants