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

feat: OAuth 2.0 Device Authorization Grant #2416 #3252

Open
wants to merge 317 commits into
base: master
Choose a base branch
from

Conversation

supercairos
Copy link

@supercairos supercairos commented Sep 8, 2022

This PR adds the Device Auth Grant to Ory/Hydra
RFC: https://www.rfc-editor.org/rfc/rfc8628

Related issue(s)

This PR is related to #2416

See also fosite PR:
https://github.com/ory/fosite/pull/701/files

Checklist

  • I have read the contributing guidelines.
  • I have referenced an issue containing the design document if my change
    introduces a new feature.
  • I am following the
    contributing code guidelines.
  • I have read the security policy.
  • I confirm that this pull request does not address a security
    vulnerability. If this pull request addresses a security. vulnerability, I
    confirm that I got green light (please contact
    [email protected]) from the maintainers to push
    the changes.
  • I have added tests that prove my fix is effective or that my feature
    works.
  • I have added or changed the documentation.

# Conflicts:
#	consent/handler.go
#	consent/manager.go
#	consent/strategy.go
#	consent/strategy_default.go
#	persistence/sql/persister_consent.go
#	persistence/sql/persister_oauth2.go
@massigori
Copy link

@aeneasr by the way of introduction I am the cloud native product manager at Canonical (the publisher of Ubuntu)
We use Hydra internally and are also interested in this feature landing in the product.
Is there anything we can do to help getting the work over the line? (We have an engineering team focused on identity management which could provide support)

@supercairos supercairos requested a review from hperl as a code owner November 24, 2023 17:22
@BuzzBumbleBee
Copy link

@aeneasr by the way of introduction I am the cloud native product manager at Canonical (the publisher of Ubuntu) We use Hydra internally and are also interested in this feature landing in the product. Is there anything we can do to help getting the work over the line? (We have an engineering team focused on identity management which could provide support)

If their is a chance to get this merged I can put some time into cleaning up my original implementation, up to now I think @supercairos has been keeping the MR alive as they are using this feature now.

I can also run you or other devs through the implementation

@alnr
Copy link
Contributor

alnr commented Nov 24, 2023

Just a quick update: this is still on our plate and we want to move this forward. Since we’ll own the code after it’s merged, we must allocate resources to really make sure we’re completely confident in how the feature integrates in hydra.

I’d expect some movement here soon.

Thanks massively again for the contribution and your patience!

@shipperizer
Copy link

shipperizer commented Nov 24, 2023

@aeneasr by the way of introduction I am the cloud native product manager at Canonical (the publisher of Ubuntu) We use Hydra internally and are also interested in this feature landing in the product. Is there anything we can do to help getting the work over the line? (We have an engineering team focused on identity management which could provide support)

@alnr just to reinforce the point above, i'm the lead engineer of the aforementioned IAM team at canonical, we are more than happy to divert some resources and help on the effort

If their is a chance to get this merged I can put some time into cleaning up my original implementation, up to now I think @supercairos has been keeping the MR alive as they are using this feature now.

I can also run you or other devs through the implementation

@BuzzBumbleBee yes please, that would be great, feel free to get in touch, here's my email [email protected] and we can take it from there

@supercairos
Copy link
Author

Hi all, would love to see this merged!
And would be happy to help on the effort :)
I hope on Monday to port our patch on the latest Hydra HEAD commit.

@supercairos
Copy link
Author

Feel free to check the sister PR also @Fosite:
ory/fosite#701

@supercairos
Copy link
Author

this code is missing quite a few tests but I can definitly work on this if it helps having this PR merged :)

@shipperizer
Copy link

@supercairos happy to contribute if help is needed, just need a bit of direction on how and where

Copy link
Contributor

@hperl hperl left a comment

Choose a reason for hiding this comment

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

Thank you very much for all the hard work! I finally managed to do a pass over the Hydra and Fosite PRs. I have left some comments, questions and ideas to improve this. Thank you for looking into this!

oauth2/handler.go Show resolved Hide resolved
oauth2/handler.go Show resolved Hide resolved
oauth2/handler.go Outdated Show resolved Hide resolved
@@ -1178,3 +1214,136 @@ func (s *DefaultStrategy) loginSessionFromCookie(r *http.Request) *flow.LoginSes

return ls
}

func (s *DefaultStrategy) requestDevice(ctx context.Context, w http.ResponseWriter, r *http.Request, req fosite.Requester) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Confusing name. Are we requesting a device?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess it makes sense now, we ask the user to either enter the user_code or confirm the code sent in the query param.

@@ -59,6 +61,9 @@ const (
IntrospectPath = "/oauth2/introspect"
RevocationPath = "/oauth2/revoke"
DeleteTokensPath = "/oauth2/tokens" // #nosec G101

// Device Grant Handler
DeviceAuthPath = "/oauth2/device/auth"
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we have two different paths, one for the user and one for the device?

E.g., /oauth/device/auth for the POST from the device, and /oauth/device/verify for the user to verify the device?

Copy link
Contributor

Choose a reason for hiding this comment

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

Additionally, I think it might be useful to handle user POST against the verification_url (https://datatracker.ietf.org/doc/html/rfc8628#section-3.3) so that a UI can render a simple form that will submit the user_code against the same endpoint.

Additionally, I think it might be useful to be able to configure a shorter version of this URL. /oauth2/device/auth is already quite long if you have to type it on a mobile, plus some origin. How about /device (as in the spec), or just /dev, or /oauth2/dev if you want to stick with the namespacing? But really, it should be configurable IMHO.

WDYT?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, that's one of the missing feature of this PR.
It shoudn't be hard to implemenent but I had to revert this particular feature because it was not working after the 2.1.1 merge.

We (at shadow) use a UI trick on the TV side to render a shorter URL (using a minified URL) but yes it should be configurable 👍

I can work on that after having fixed all the other PR reviews :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Additionally, I think it might be useful to handle user POST against the verification_url (https://datatracker.ietf.org/doc/html/rfc8628#section-3.3) so that a UI can render a simple form that will submit the user_code against the same endpoint.

Please ignore this remark, I now understand your approach with the device verifier, which is using a POST to the UI and then an admin API to bind the device to the flow.

}

// FIXME: Add Doc
func (h *Handler) performOAuth2DeviceAuthorizationFlow(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Tests?

Copy link
Author

Choose a reason for hiding this comment

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

Sure it's missing quite a lots of tests :)

I'll add them asap :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you!

// Responses:
// 200: deviceAuthorization
// default: errorOAuth2
func (h *Handler) performOAuth2DeviceFlow(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Tests?

@@ -217,6 +217,60 @@ func (p *Persister) GetConsentRequest(ctx context.Context, challenge string) (*f
return f.GetConsentRequest(), nil
}

func (p *Persister) CreateDeviceGrantRequest(ctx context.Context, req *flow.DeviceGrantRequest) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove these if you agree we can use user_code.

Otherwise, we'll need tests ;).

Copy link
Contributor

Choose a reason for hiding this comment

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

OK, now I know we need this.

/* #nosec G201 table is static */
return sqlcon.HandleError(
p.QueryWithNetwork(ctx).Where("client_id=?", clientID).Delete(&OAuth2RequestSQL{Table: sqlTableAccess}),
)
}

func (p *Persister) CreateDeviceCodeSession(ctx context.Context, signature string, requester fosite.Requester) (err error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Tests for all new methods in this file please.

sqlTableRefresh tableName = "refresh"
sqlTableCode tableName = "code"
sqlTablePKCE tableName = "pkce"
sqlTableDeviceCode tableName = "device_code"
Copy link
Contributor

Choose a reason for hiding this comment

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

Please check if we really need two tables here :).

Copy link
Author

Choose a reason for hiding this comment

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

In fact, the best would be to use the flow table as it can be seen as part of a login flow but since this MR was done before the 2.x we never took the time to update the database scheme.

But yes i believe there can be only one DB for both device_code and user_code as they are tied together

@supercairos
Copy link
Author

Here are some tools help develop this feature:

  1. A fake client to test the DAG: https://github.com/supercairos/oauth-device-flow-client-sample
  2. A Hydra-UI that implement the DAG: https://github.com/supercairos/hydra-login-consent-node

@lltr
Copy link

lltr commented Dec 8, 2023

Looking forward to this being out! Can't find any other open source provider supporting this.

@RomanHotsiy
Copy link

Hey folks 👋. Any plans to get it moving? Do you need any help?

@nsklikas nsklikas mentioned this pull request Sep 30, 2024
7 tasks
@nsklikas nsklikas mentioned this pull request Dec 18, 2024
7 tasks
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.