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

Sps-Service Header Proposal #99

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

nicholas-s-perkins
Copy link
Contributor

No description provided.

@nicholas-s-perkins nicholas-s-perkins requested a review from a team as a code owner December 12, 2024 00:22
Comment on lines +546 to +553
- `Sps-User-Agent` **MUST** be provided for all API requests that cannot include serviceName and serviceId in the `User-Agent`.
- The header value **MUST** follow this format `serviceName[/serviceVersion] [sdk[/sdkVersion ](serviceTechRegistryId)`
- `^(?<NAME>[a-z0-9_-]{1,60})(?:/[^ ]+)? (?<SDK>[^ /]+(?:/[^ /]+)?)? ?\((?<SERVICEID>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\)$`
- The header value **SHOULD** contain the SDK of the client.
- It is expected to be rare that a service includes its version in the header, but it is an allowed option.
- Requests with an invalid `Sps-User-Agent` **MUST** return a `400` response status code.
- A serviceName **MAY** use `_` to delineate sub-clients of that service.
- An API is expected to be able to parse User-Agent or this header for tech-registry and serviceName validation.
Copy link
Contributor Author

@nicholas-s-perkins nicholas-s-perkins Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Including the regex as more of a proposal, assuming we might want to debate this.
I think it is important that it follows a very strict format to be useful, though, as it doesn't have to be as permissive as User-Agent.

Comment on lines +535 to +544
**Description**:
`Sps-User-Agent` is a direct augmentation of `User-Agent` and is intended to be paired with it, following
the format of the SPS [Authentication and Authorization Guardrail](https://atlassian.spscommerce.com/wiki/display/Guardrails/[Authentication+and+Authorization).
Typically, an API should require the `User-Agent` header, and that it **SHOULD** follow the format
`serviceName[/serviceVersion] [sdk[/sdkVersion ](serviceTechRegistryId)` to include the valid tech-registry
serviceName and serviceId of the client.
However, is common for some clients, such as web-browsers, to lock a client out of setting the `User-Agent`.
Thus, `User-Agent` cannot reliably encode actually identifying information for general applications.
Furthermore, auth tokens also do not include this information, and it is also common to forward a user_token, where
what an API would rather know is not what "user" but what "client" is making the request.
Copy link
Contributor Author

@nicholas-s-perkins nicholas-s-perkins Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be a bit wordy, but this is my attempt to explain the long causality that causes one to want this header in the critical API space.

parcel-service and NCS already support a custom header like this, which is X-User-Agent-Override, so this is my attempt to standardize that (and include it as a requirement for any UI that uses payload-storage-service). We find it very valuable for understanding UI behavior.

One thing this doesn't quite call out is that I think any API that implements this header then should also expect User-Agent to have a good format. Like either the User-Agent has the format, or Sps-User-Agent does, but they both can't.
So the cascade goes:

  1. Check Sps-User-Agent first, set as Agent value.
  2. If Sps-User-Agent not present, set User-Agent as Agent value.
  3. If Agent value is invalid format, reject with a 400

This ends up being a bit distinct at the recommendation of a 403 for an absent User-Agent. Absent agent (403) vs present but invalid agent (400).

@@ -527,6 +527,47 @@ Sps-Execution-Context: // values must be at least a character l
Sps-Execution-Context: 1 // valid, but SHOULD be human-readable.
```

#### Sps-User-Agent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User-Agent traditionally identifies the user-facing client, such as a browser or device. Service-to-service communication tracking is a distinct concept in my opinion, and conflating the two can lead to confusion. A unique header name clarifies its purpose, making it easier to explain, document, and enforce. I love this concept and the details, but I think we should workshop a different name for the header. (SPS-Service, SPS-Origin-Service, SPS-Service-Client, etc)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be SPS specific? If an external client uses an API through a browser, I assume we would still want it provided.

Copy link
Contributor Author

@nicholas-s-perkins nicholas-s-perkins Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Service-to-service communication tracking is a distinct concept in my opinion, and conflating the two can lead to confusion.

It could be, but our guardrails already pushes apps to treat User-Agent as the place to put serviceName/serviceId, and this is really just trying to conceive of a way to support that for web-uis (which, we don't always think of as a 'service' but for all intents and purposes of this they are). The format for it is right out of our guardrails for User-Agent.
As a point of reference, we already require teams to use a good user-agent for parcel-service, but we can't enforce it, so it's an immense amount of work to track down new users that refuse to follow our guidelines and have them update their agents.
What I actually wanted to do was just force the regex on User-Agent itself for any new API we make, but Chrome hates us. So the squaring of that is to enforce the regex on both User-Agent or Sps-User-Agent, where Sps-User-Agent merely exists for things that cannot set a good User-Agent.

I feel like the naming denotes how they are essentially the exact same concept, it's not a conflation of concepts they are intended to be the same concept. But, I'm not super wedded to it. Practically, most users are going to find out about what the headeris called because my API rejects their web requests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see different reasons where the continued usage of User-Agent makes sense as is to denote client details about the "instance" make the request, such as a browser (browser, web app), python version (backend), etc. Using something more structured and designated for service metadata allows for the operation of the normal user agent to continue without confusion for "SPS-User-Agent". Additionally, I think the service metadata implies something more static about the application consumer (not the user or context of the user).

I'd advocate for something specifically for this purpose:
SPS-Service: xxxx-xxxx-xxxxx-xxxx (my-service-name)

This data as an exact term can be filled in via other infrastructure in the future as well, such as Atlas egress. Thoughts?

Copy link
Contributor Author

@nicholas-s-perkins nicholas-s-perkins Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I've thought about, I'm really just trying to solve a problem with wanting service-name + service-id.
I'm somewhat agnostic as to the exact approach, but I do want to require it in some way for my new service since I do really want to solve the ambiguous caller problem that we have in our network.

If we want to shift away from User-Agent as an app identifier, that also can work. It's maybe interesting then to rethink User-Agent as more of a "what is this tech thing" question. Like it's more what tells you if something is a browser or a java/python thing. In that design lens, then, my app might not even care if something has a User-Agent for the most part.

Thinking through it a bit, one simplification could be two headers:

Sps-Service-Name: my-service
Sps-Service-Id: d8289b6e-5af7-4568-a1be-ebdf872b2c44

Certainly would make it easier to parse and there would be no ambiguity about the format, but also that's kind of a bummer to require two headers like this.

Combining them into your idea of Sps-Service is a bit harder to parse, but probably more ergonomic for clients. Then you have to debate format a bit. A lot of ways you could slice it, but space separated is probably the most simple (?) and we don't allow them for name nor ids. It also matches how other headers separate values like User-Agent and is trivial to parse. I don't really care what the order is, but if given the choice I would probably pick {name} {id} as it reads a bit more naturally for left-right language readers to me and matches what was our user-agent order guideline:

Sps-Service: my-service d8289b6e-5af7-4568-a1be-ebdf872b2c44
Sps-Service: d8289b6e-5af7-4568-a1be-ebdf872b2c44 my-service

Worth noting, we did come up with a nomenclature in some of the clients called the "app agent", which we could use instead but that was in part to align with injecting the values into User-Agent and kind of matching the naming that it was an "Agent".

So Sps-Service is maybe the best option, so I'd also vote for that. I also don't think it prevents a future of if Sps-Service-Id becomes a thing at a later point, and could work as a augmentation of it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like it's more what tells you if something is a browser or a java/python thing. In that design lens, then, my app might not even care if something has a User-Agent for the most part.

My intent, perspective is to try and divorce this from being a user-agent solution/problem. That aside, user-agent is required in our API Standards today, and is enforced header you have to pass on all Network API requests as part of the incoming gateway.

Certainly would make it easier to parse and there would be no ambiguity about the format, but also that's kind of a bummer to require two headers like this.

Agreed, I think we can lean on a single format with a single header for now that combines the name and ID. That being said, can you provide more context on the need for "name"? It is something that is not immutable / can be changed in tech registry usage. I know its convenient to have, are there other implications of not having it? Does parcel service require the name today? Should name be optional, while service id required?

@travisgosselin travisgosselin changed the title Sps-User-Agent Header Proposal Sps-Service Header Proposal Dec 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants