Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitdd8db0b

Browse files
committed
github oauth2 device flow backend
1 parent53f0007 commitdd8db0b

File tree

6 files changed

+130
-6
lines changed

6 files changed

+130
-6
lines changed

‎cli/server.go‎

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -677,12 +677,13 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
677677
}
678678
}
679679

680-
ifvals.OAuth2.Github.ClientSecret!="" {
680+
ifvals.OAuth2.Github.ClientSecret!=""||vals.OAuth2.Github.DeviceFlow.Value(){
681681
options.GithubOAuth2Config,err=configureGithubOAuth2(
682682
oauthInstrument,
683683
vals.AccessURL.Value(),
684684
vals.OAuth2.Github.ClientID.String(),
685685
vals.OAuth2.Github.ClientSecret.String(),
686+
vals.OAuth2.Github.DeviceFlow.Value(),
686687
vals.OAuth2.Github.AllowSignups.Value(),
687688
vals.OAuth2.Github.AllowEveryone.Value(),
688689
vals.OAuth2.Github.AllowedOrgs,
@@ -1831,8 +1832,10 @@ func configureCAPool(tlsClientCAFile string, tlsConfig *tls.Config) error {
18311832
returnnil
18321833
}
18331834

1835+
// TODO: convert the argument list to a struct, it's easy to mix up the order of the arguments
1836+
//
18341837
//nolint:revive // Ignore flag-parameter: parameter 'allowEveryone' seems to be a control flag, avoid control coupling (revive)
1835-
funcconfigureGithubOAuth2(instrument*promoauth.Factory,accessURL*url.URL,clientID,clientSecretstring,allowSignups,allowEveryonebool,allowOrgs []string,rawTeams []string,enterpriseBaseURLstring) (*coderd.GithubOAuth2Config,error) {
1838+
funcconfigureGithubOAuth2(instrument*promoauth.Factory,accessURL*url.URL,clientID,clientSecretstring,deviceFlow,allowSignups,allowEveryonebool,allowOrgs []string,rawTeams []string,enterpriseBaseURLstring) (*coderd.GithubOAuth2Config,error) {
18361839
redirectURL,err:=accessURL.Parse("/api/v2/users/oauth2/github/callback")
18371840
iferr!=nil {
18381841
returnnil,xerrors.Errorf("parse github oauth callback url: %w",err)
@@ -1898,6 +1901,17 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
18981901
returngithub.NewClient(client),nil
18991902
}
19001903

1904+
vardeviceAuth*externalauth.DeviceAuth
1905+
ifdeviceFlow {
1906+
deviceAuth=&externalauth.DeviceAuth{
1907+
Config:instrumentedOauth,
1908+
ClientID:clientID,
1909+
TokenURL:endpoint.TokenURL,
1910+
Scopes: []string{"read:user","read:org","user:email"},
1911+
CodeURL:endpoint.DeviceAuthURL,
1912+
}
1913+
}
1914+
19011915
return&coderd.GithubOAuth2Config{
19021916
OAuth2Config:instrumentedOauth,
19031917
AllowSignups:allowSignups,
@@ -1941,6 +1955,19 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
19411955
team,_,err:=api.Teams.GetTeamMembershipBySlug(ctx,org,teamSlug,username)
19421956
returnteam,err
19431957
},
1958+
DeviceFlowEnabled:deviceFlow,
1959+
ExchangeDeviceCode:func(ctx context.Context,deviceCodestring) (*oauth2.Token,error) {
1960+
if!deviceFlow {
1961+
returnnil,xerrors.New("device flow is not enabled")
1962+
}
1963+
returndeviceAuth.ExchangeDeviceCode(ctx,deviceCode)
1964+
},
1965+
AuthorizeDevice:func(ctx context.Context) (*codersdk.ExternalAuthDevice,error) {
1966+
if!deviceFlow {
1967+
returnnil,xerrors.New("device flow is not enabled")
1968+
}
1969+
returndeviceAuth.AuthorizeDevice(ctx)
1970+
},
19441971
},nil
19451972
}
19461973

‎coderd/coderd.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,7 @@ func New(options *Options) *API {
10871087
r.Post("/validate-password",api.validateUserPassword)
10881088
r.Post("/otp/change-password",api.postChangePasswordWithOneTimePasscode)
10891089
r.Route("/oauth2",func(r chi.Router) {
1090+
r.Get("/github/device",api.userOAuth2GithubDevice)
10901091
r.Route("/github",func(r chi.Router) {
10911092
r.Use(
10921093
httpmw.ExtractOAuth2(options.GithubOAuth2Config,options.HTTPClient,nil),

‎coderd/httpmw/oauth2.go‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,16 @@ func ExtractOAuth2(config promoauth.OAuth2Config, client *http.Client, authURLOp
167167

168168
oauthToken,err:=config.Exchange(ctx,code)
169169
iferr!=nil {
170-
httpapi.Write(ctx,rw,http.StatusInternalServerError, codersdk.Response{
171-
Message:"Internal error exchanging Oauth code.",
172-
Detail:err.Error(),
170+
errorCode:=http.StatusInternalServerError
171+
detail:=err.Error()
172+
ifdetail=="authorization_pending" {
173+
// In the device flow, the token may not be immediately
174+
// available. This is expected, and the client will retry.
175+
errorCode=http.StatusBadRequest
176+
}
177+
httpapi.Write(ctx,rw,errorCode, codersdk.Response{
178+
Message:"Failed exchanging Oauth code.",
179+
Detail:detail,
173180
})
174181
return
175182
}

‎coderd/userauth.go‎

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,12 +748,32 @@ type GithubOAuth2Config struct {
748748
ListOrganizationMembershipsfunc(ctx context.Context,client*http.Client) ([]*github.Membership,error)
749749
TeamMembershipfunc(ctx context.Context,client*http.Client,org,team,usernamestring) (*github.Membership,error)
750750

751+
DeviceFlowEnabledbool
752+
ExchangeDeviceCodefunc(ctx context.Context,deviceCodestring) (*oauth2.Token,error)
753+
AuthorizeDevicefunc(ctx context.Context) (*codersdk.ExternalAuthDevice,error)
754+
751755
AllowSignupsbool
752756
AllowEveryonebool
753757
AllowOrganizations []string
754758
AllowTeams []GithubOAuth2Team
755759
}
756760

761+
func (c*GithubOAuth2Config)Exchange(ctx context.Context,codestring,opts...oauth2.AuthCodeOption) (*oauth2.Token,error) {
762+
if!c.DeviceFlowEnabled {
763+
returnc.OAuth2Config.Exchange(ctx,code,opts...)
764+
}
765+
returnc.ExchangeDeviceCode(ctx,code)
766+
}
767+
768+
func (c*GithubOAuth2Config)AuthCodeURL(statestring,opts...oauth2.AuthCodeOption)string {
769+
if!c.DeviceFlowEnabled {
770+
returnc.OAuth2Config.AuthCodeURL(state,opts...)
771+
}
772+
// This is an absolute path in the Coder app. The device flow is orchestrated
773+
// by the Coder frontend, so we need to redirect the user to the device flow page.
774+
return"/login/device?state="+state
775+
}
776+
757777
// @Summary Get authentication methods
758778
// @ID get-authentication-methods
759779
// @Security CoderSessionToken
@@ -786,6 +806,53 @@ func (api *API) userAuthMethods(rw http.ResponseWriter, r *http.Request) {
786806
})
787807
}
788808

809+
// @Summary Get Github device auth.
810+
// @ID get-github-device-auth
811+
// @Security CoderSessionToken
812+
// @Produce json
813+
// @Tags Users
814+
// @Success 200 {object} codersdk.ExternalAuthDevice
815+
// @Router /users/oauth2/github/device [get]
816+
func (api*API)userOAuth2GithubDevice(rw http.ResponseWriter,r*http.Request) {
817+
var (
818+
ctx=r.Context()
819+
auditor=api.Auditor.Load()
820+
aReq,commitAudit=audit.InitRequest[database.APIKey](rw,&audit.RequestParams{
821+
Audit:*auditor,
822+
Log:api.Logger,
823+
Request:r,
824+
Action:database.AuditActionLogin,
825+
})
826+
)
827+
aReq.Old= database.APIKey{}
828+
defercommitAudit()
829+
830+
ifapi.GithubOAuth2Config==nil {
831+
httpapi.Write(ctx,rw,http.StatusBadRequest, codersdk.Response{
832+
Message:"Github OAuth2 is not enabled.",
833+
})
834+
return
835+
}
836+
837+
if!api.GithubOAuth2Config.DeviceFlowEnabled {
838+
httpapi.Write(ctx,rw,http.StatusBadRequest, codersdk.Response{
839+
Message:"Device flow is not enabled for Github OAuth2.",
840+
})
841+
return
842+
}
843+
844+
deviceAuth,err:=api.GithubOAuth2Config.AuthorizeDevice(ctx)
845+
iferr!=nil {
846+
httpapi.Write(ctx,rw,http.StatusInternalServerError, codersdk.Response{
847+
Message:"Failed to authorize device.",
848+
Detail:err.Error(),
849+
})
850+
return
851+
}
852+
853+
httpapi.Write(ctx,rw,http.StatusOK,deviceAuth)
854+
}
855+
789856
// @Summary OAuth 2.0 GitHub Callback
790857
// @ID oauth-20-github-callback
791858
// @Security CoderSessionToken
@@ -1016,7 +1083,14 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
10161083
}
10171084

10181085
redirect=uriFromURL(redirect)
1019-
http.Redirect(rw,r,redirect,http.StatusTemporaryRedirect)
1086+
ifapi.GithubOAuth2Config.DeviceFlowEnabled {
1087+
// In the device flow, the redirect is handled client-side.
1088+
httpapi.Write(ctx,rw,http.StatusOK, codersdk.OAuth2DeviceFlowCallbackResponse{
1089+
RedirectURL:redirect,
1090+
})
1091+
}else {
1092+
http.Redirect(rw,r,redirect,http.StatusTemporaryRedirect)
1093+
}
10201094
}
10211095

10221096
typeOIDCConfigstruct {

‎codersdk/deployment.go‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,7 @@ type OAuth2Config struct {
505505
typeOAuth2GithubConfigstruct {
506506
ClientID serpent.String`json:"client_id" typescript:",notnull"`
507507
ClientSecret serpent.String`json:"client_secret" typescript:",notnull"`
508+
DeviceFlow serpent.Bool`json:"device_flow" typescript:",notnull"`
508509
AllowedOrgs serpent.StringArray`json:"allowed_orgs" typescript:",notnull"`
509510
AllowedTeams serpent.StringArray`json:"allowed_teams" typescript:",notnull"`
510511
AllowSignups serpent.Bool`json:"allow_signups" typescript:",notnull"`
@@ -1572,6 +1573,16 @@ func (c *DeploymentValues) Options() serpent.OptionSet {
15721573
Annotations: serpent.Annotations{}.Mark(annotationSecretKey,"true"),
15731574
Group:&deploymentGroupOAuth2GitHub,
15741575
},
1576+
{
1577+
Name:"OAuth2 GitHub Device Flow",
1578+
Description:"Enable device flow for Login with GitHub.",
1579+
Flag:"oauth2-github-device-flow",
1580+
Env:"CODER_OAUTH2_GITHUB_DEVICE_FLOW",
1581+
Value:&c.OAuth2.Github.DeviceFlow,
1582+
Group:&deploymentGroupOAuth2GitHub,
1583+
YAML:"deviceFlow",
1584+
Default:"false",
1585+
},
15751586
{
15761587
Name:"OAuth2 GitHub Allowed Orgs",
15771588
Description:"Organizations the user must be a member of to Login with GitHub.",

‎codersdk/oauth2.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,7 @@ func (c *Client) RevokeOAuth2ProviderApp(ctx context.Context, appID uuid.UUID) e
227227
}
228228
returnnil
229229
}
230+
231+
typeOAuth2DeviceFlowCallbackResponsestruct {
232+
RedirectURLstring`json:"redirect_url"`
233+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp