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

🚧 Add support for OAuth device-code login 🚧 #5221

Closed
wants to merge 1 commit into from

Conversation

laurazard
Copy link
Member

- What I did

WIP PR for discussion that adds support for the device-code flow OAuth login, when authenticating against the official registry.

I'll follow up with another PR to explore implementing this at the credential store layer/using a new credential store.

- How I did it

Added cli/internal/oauth, which includes a new OAuthManager that is capable of fulfilling a device-code flow against the official registry.

Fetching credentials remains the same, as the returned access token can be transparently used in place of the password when authenticating.

Login and logout flows now defer to OAuthManager when running against the official registry.

- How to verify it

Run docker login, and check your credentials store to find the stored access tokens. Attempt to do an operation which requires authentication (such as pushing an image to your repo) and verify it works.

- Description for the changelog

Added support for the device-code flow OAuth login, when authenticating against the official registry.

- A picture of a cute animal (not mandatory but encouraged)

Screenshot 2024-07-01 at 13 57 11

@laurazard laurazard force-pushed the auth-device-flow branch 2 times, most recently from a26accb to 819df9f Compare July 3, 2024 11:42
@codecov-commenter
Copy link

codecov-commenter commented Jul 3, 2024

Codecov Report

Attention: Patch coverage is 52.19298% with 109 lines in your changes missing coverage. Please review.

Project coverage is 61.39%. Comparing base (6abed4e) to head (4029dbc).
Report is 4 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5221      +/-   ##
==========================================
- Coverage   61.48%   61.39%   -0.09%     
==========================================
  Files         298      302       +4     
  Lines       20813    21037     +224     
==========================================
+ Hits        12797    12916     +119     
- Misses       7104     7192      +88     
- Partials      912      929      +17     

@laurazard laurazard force-pushed the auth-device-flow branch 5 times, most recently from dec275e to 8205723 Compare July 3, 2024 14:19
@laurazard
Copy link
Member Author

Recording with the UX/copy changes:

Screen.Recording.2024-07-03.at.15.12.35.mov

@laurazard laurazard force-pushed the auth-device-flow branch 2 times, most recently from d490cf3 to 86ccab5 Compare July 3, 2024 16:01
@dvdksn
Copy link
Contributor

dvdksn commented Jul 3, 2024

you're probably doing this already... but can we hide the "Authenticating with existing credentials" (and error) bit if that isn't happening?

@dvdksn dvdksn closed this Jul 3, 2024
@dvdksn dvdksn reopened this Jul 3, 2024
@dvdksn
Copy link
Contributor

dvdksn commented Jul 3, 2024

misclick 😓

@laurazard
Copy link
Member Author

laurazard commented Jul 3, 2024

you're probably doing this already... but can we hide the "Authenticating with existing credentials" (and error) bit if that isn't happening?

Yep! That message should only show up when we're actually logging in with existing credentials, what happened in that video was misleading because in truth we already had credentials, the "first" login (with those credentials) just failed for different reasons. I'll update the recording.

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

🙈 also gave it a quick try what it would look like without that host.Info() call;

Screenshot 2024-07-04 at 11 12 20

)

func NewManager(store credentials.Store) (*OAuthManager, error) {
hostinfo, err := host.Info()
Copy link
Member

Choose a reason for hiding this comment

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

I was, erm, curious where all the extra dependencies originated from 🙈;

  • github.com/tklauser/go-sysconf
  • github.com/tklauser/go-sysconf (number of online CPUs?)
  • github.com/shoenig/go-m1cpu (CPU info for m1 CPUs)
  • github.com/power-devops/perfstat (handling of libperfstat ??
  • github.com/go-ole/go-ole (loading of Windows OLE objects)
  • github.com/yusufpapurcu/wmi (WQL interface to Windows Management Instrumentation (WMI))

And those all look to be related to this single host.Info(); gopsutil/host.Info() is doing a lot of things; https://github.com/shirou/gopsutil/blob/865b8c3f58d6ee4c47dab074fbf3c4b037abfd96/host/host.go#L65-L117

cli/internal/oauth/manager/util.go Outdated Show resolved Hide resolved
@laurazard laurazard force-pushed the auth-device-flow branch 2 times, most recently from 49ba2bf to ed4e811 Compare July 4, 2024 09:35
Comment on lines +190 to +210
const (
registryAuthKey = "https://index.docker.io/v1/"
accessTokenKey = "access-token"
refreshTokenKey = "refresh-token"
)

func (m *OAuthManager) fetchTokensFromStore() (access, refresh string, err error) {
accessAuth, err := m.credStore.Get(registryAuthKey + accessTokenKey)
if err != nil {
return access, refresh, fmt.Errorf("failed to fetch access token: %w", err)
}
access = accessAuth.Password

refreshAuth, err := m.credStore.Get(registryAuthKey + refreshTokenKey)
if err != nil {
return access, refresh, fmt.Errorf("failed to fetch refresh token: %w", err)
}
refresh = refreshAuth.Password

return
}
Copy link
Collaborator

@vvoland vvoland Jul 4, 2024

Choose a reason for hiding this comment

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

We may need some other way to construct the access and refresh token keys (prepend as a subdomain?), or make sure that there's no code that takes the registry hostname into account - like #3599 + #5195.
Otherwise, the access/refresh token could end up being used as an actual auth token.

I haven't investigated it in detail, so it might not be an actual issue in this case, but thought it's worth bringing up 😅

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, that code only does that for non-official registries, so it should be fine (and indeed, that works). We can make sure it continues to work by adding some e2e tests, but maybe we can think of some other way to store these.

I still also want to try to implement some of these at the credstore level, so I'll see how that looks too/if this still matters.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah while it might not be a problem for the CLI code itself, I'm curious if other credential helpers might also do something like this.

Implementing these extra tokens on the credential store level would be nice indeed.

Copy link
Member Author

Choose a reason for hiding this comment

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

We can check! What I was going to do was still at the CLI level, but as a wrapper to the CLI "credential store" implementations.

Signed-off-by: Laura Brehm <[email protected]>
@laurazard
Copy link
Member Author

Superseded by #5244 or #5245

@laurazard laurazard closed this Jul 8, 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.

5 participants