Open erlend-sh opened 4 weeks ago
Can I get assigned?
I'm really interested in pushing this for Rauthy.
I was looking into writing something similar as Rauthy just only for Atproto oauth. An integration into Rauthy would save a lot of work, but my usecase will be a little different from yours.
I and probably most people that will use this Atproto oauth integration will need access to the atproto access tokens (and some additional data), to make requests to bluesky (or other pds servers). This will require a little bit more work than just adding it as another authentication provider to Rauthy.
Nice!
Well for starters maybe @avdb13 can ask you for a review when he’s done with the essentials of the ‘add authentication provider to Rauthy’ task, then maybe we’ll let you take it from there.
I also have some experience with Atrium Api and I read some of the new oauth code that was just commited. I can definitely help there if needed. The additional work on the Atrium Api crate is blocking the work on this though. I'm going to try and figure out what is required and which changes need to be made to accommodate for the usecase, where an external authentication provider like Rauthy is used with a server that needs to talk to the atproto network.
Sounds good. We’re also hanging out on the community Discord in the #rust channel, which for now has led us here:
Is bluesky-social/atproto#2756 really a blocker? Do we need multiple redirect uri's for Rauthy?
I’m not sure that it’s a blocker, I’m just saying that’s as far as the explorative development has gotten us this far.
Alright, I made a draft but it's not ready for testing yet. From what I understood, these are exact steps (quoted from the implemementation guide) that should take place when "Login with Bluesky" is chosen:
@jay.bsky.social
) did=did:plc:z72i7hdynmk6r22z27h6tvur
)https://xn--ls8h.test
) Retrieve the PDS URI, depending on the information from the previous step:
handle -> DID document
: request the domain's TXT DNS record, or make a request to another PDS' /.well-known/atproto-did
endpoint. If the DID method is PLC, send a query to a preconfigured PLC directory, if the method is web call the hostname's /.well-known/did.json
.DID document -> PDS
: under the document's service
array, we wanna match the first entry where id
ends with #atproto_pds
, and type
matches AtprotoPersonalDataServer
. The first matching entry should be always be used.Retrieve the PDS' authorization server URI:
PDS -> protected resource metadata
: fetch the PDS' /.well-known/oauth-protected-resource
, the document should contain a field authorization_servers
with a single entry which should point to the user's authorization server (PDS or entryway).
protected resource metadata -> authorization server metadata
: fetch the PDS' /.well-known/oauth-authorization-server
, the document's protected_resources
should contain the protected resource metadata's URI.make a Pushed Authorization Request: // TODO
- Retrieve the PDS URI, depending on the information from the previous step:
handle -> DID document
: request the domain's TXT DNS record, or make a request to another PDS'/.well-known/atproto-did
endpoint. If the DID method is PLC, send a query to a preconfigured PLC directory, if the method is web call the hostname's/.well-known/did.json
.DID document -> PDS
: under the document'sservice
array, we wanna match the first entry whereid
ends with#atproto_pds
, andtype
matchesAtprotoPersonalDataServer
. The first matching entry should be always be used.
- Retrieve the PDS' authorization server URI:
PDS -> protected resource metadata
: fetch the PDS'/.well-known/oauth-protected-resource
, the document should contain a fieldauthorization_servers
with a single entry which should point to the user's authorization server (PDS or entryway).protected resource metadata -> authorization server metadata
: fetch the PDS'/.well-known/oauth-authorization-server
, the document'sprotected_resources
should contain the protected resource metadata's URI.
- make a Pushed Authorization Request: // TODO
This should all be taken care of by #atrium/feature/oauth.
Atproto oauth is based on OAuth 2.1 but there are additional settings, so sadly it's not compatible with the auth_providers. Maybe the data model can be adjusted to combine them, but it should be easier to only do this in the UI.
Also additional endpoints are needed for Client Metadate and JWK's. atrium-oauth-wasm endpoints
Maybe we could try and get some inspiration from the FedCM integration. It also needs Client Metadata so we will need a similar endpoint as this
Atproto oauth also needs state to be stored before redirecting to the authentication server, which needs to be available when the callback is called. A cookie like here could be used for that.
- Ask the user for one of the following pieces of information:
- handle (
@jay.bsky.social
)- DID (
did=did:plc:z72i7hdynmk6r22z27h6tvur
)- PDS (
https://xn--ls8h.test
)- AT URI (do we wanna support this?)
I think it would be important to properly verify this user input. I have tested other atproto oauth apps and there was one that failed if the input was not a proper handle or pds Domain.
handle (@jay.bsky.social)
That's the only login method we need for a POC. 99.99% of users are only aware of their handle.
Configurations needed:
This should all be taken care of by #atrium/feature/oauth.
Indeed, the purpose was documenting this MR and figuring out whether something is missing, i.e. application_type
in our client metadata should be configurable to qualify as a confidential client.
Atproto oauth is based on OAuth 2.1 but there are additional settings, so sadly it's not compatible with the auth_providers. Maybe the data model can be adjusted to combine them, but it should be easier to only do this in the UI.
That's what I found out as well, I'm just clueless as to how we should make atproto configurable in the UI.
Also additional endpoints are needed for Client Metadate and JWK's. atrium-oauth-wasm endpoints
Great catch, I think atproto should keep a separate JWKS since invalidating a key means invalidating tokens bound to it.
Atproto oauth also needs state to be stored before redirecting to the authentication server, which needs to be available when the callback is called. A cookie like here could be used for that.
Thanks for the clarification, I was having a hard time figuring out whether a cookie is necessary at all since there's no mentioning of it. What shouls be stored in the cookie though? I thought atrium used its server-side state store to handle the callback.
I think it would be important to properly verify this user input. I have tested other atproto oauth apps and there was one that failed if the input was not a proper handle or pds Domain.
I thought the specification had quite strict validation rules here but please let me know if there's edge cases here.
Thanks for your sharing your thoughts.
how we should make atproto configurable in the UI.
What exactly needs to be configurable about atproto in the UI? I don’t think we need any configurability for the POC stage.
how we should make atproto configurable in the UI.
What exactly needs to be configurable about atproto in the UI? I don’t think we need any configurability for the POC stage.
Yes. Just requires a configuration option for now.
Atproto oauth also needs state to be stored before redirecting to the authentication server, which needs to be available when the callback is called. A cookie like here could be used for that.
Thanks for the clarification, I was having a hard time figuring out whether a cookie is necessary at all since there's no mentioning of it. What should be stored in the cookie though? I thought atrium used its server-side state store to handle the callback.
Atrium oauth currently only provides a memory based state store. This is not replicated though, so users could run into issues with high availability setups. atrium-oauth-wasm used Deno's replicated Key-Value-Store to solve this problem. Rauthy uses a cookie for state when authenticating with a provider here. I just thought that we should use the same approach. The most optimal way of storing this state would be a replicated database like Redis (Valkey), but I'm kind of hesitant to add another database with it's required client library to the project. (there are alread like 600 dependencys)
I think it would be important to properly verify this user input. I have tested other atproto oauth apps and there was one that failed if the input was not a proper handle or pds Domain.
I thought the specification had quite strict validation rules here but please let me know if there's edge cases here.
My mistake. I thought atrium oauth does not do any validation, but it does. Just try invalid handles as inputs for atrium-oauth-wasm demo and an error message will be returned.
Atproto oauth is based on OAuth 2.1 but there are additional settings, so sadly it's not compatible with the auth_providers. Maybe the data model can be adjusted to combine them, but it should be easier to only do this in the UI.
That's what I found out as well, I'm just clueless as to how we should make atproto configurable in the UI.
Yeah, I also already spent some time thinking about this. The differences are too great to combine both data models. So the only options I see are Option A: display as an additional provider separate of the other ones Option B: unify them in the UI B would probably the best for users
The work-in-progress can now be tracked and further commented on here:
work-in-progress for atrium has been moved to https://github.com/sugyan/atrium/pull/242, apologies
Essential reading: https://docs.bsky.app/blog/oauth-atproto https://atproto.com/specs/oauth
The immediate goal is support for Bluesky-login in Rauthy. In other words we wanna include ‘Login with Bluesky’ as an option here: https://a.weird.one/login
The closest reference for it already in Rauthy is GitHub-login, which also doesn’t use OIDC.
@anderspitman said:
We can probably just use this:
Other useful references here: