mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
feat: add ability for users to convert their password login type to oauth/github login (#8105)
* Currently toggled by experiment flag --------- Co-authored-by: Bruno Quaresma <bruno@coder.com>
This commit is contained in:
@ -16,8 +16,9 @@ import (
|
||||
type oauth2StateKey struct{}
|
||||
|
||||
type OAuth2State struct {
|
||||
Token *oauth2.Token
|
||||
Redirect string
|
||||
Token *oauth2.Token
|
||||
Redirect string
|
||||
StateString string
|
||||
}
|
||||
|
||||
// OAuth2Config exposes a subset of *oauth2.Config functions for easier testing.
|
||||
@ -91,13 +92,24 @@ func ExtractOAuth2(config OAuth2Config, client *http.Client, authURLOpts map[str
|
||||
|
||||
if code == "" {
|
||||
// If the code isn't provided, we'll redirect!
|
||||
state, err := cryptorand.String(32)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error generating state string.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
var state string
|
||||
// If this url param is provided, then a user is trying to merge
|
||||
// their account with an OIDC account. Their password would have
|
||||
// been required to get to this point, so we do not need to verify
|
||||
// their password again.
|
||||
oidcMergeState := r.URL.Query().Get("oidc_merge_state")
|
||||
if oidcMergeState != "" {
|
||||
state = oidcMergeState
|
||||
} else {
|
||||
var err error
|
||||
state, err = cryptorand.String(32)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error generating state string.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
http.SetCookie(rw, &http.Cookie{
|
||||
@ -158,8 +170,9 @@ func ExtractOAuth2(config OAuth2Config, client *http.Client, authURLOpts map[str
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, oauth2StateKey{}, OAuth2State{
|
||||
Token: oauthToken,
|
||||
Redirect: redirect,
|
||||
Token: oauthToken,
|
||||
Redirect: redirect,
|
||||
StateString: state,
|
||||
})
|
||||
next.ServeHTTP(rw, r.WithContext(ctx))
|
||||
})
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/moby/moby/pkg/namesgenerator"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/oauth2"
|
||||
@ -125,4 +126,21 @@ func TestOAuth2(t *testing.T) {
|
||||
// testOAuth2Provider does this job for us.
|
||||
require.NotEmpty(t, location)
|
||||
})
|
||||
t.Run("PresetConvertState", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
customState := namesgenerator.GetRandomName(1)
|
||||
req := httptest.NewRequest("GET", "/?oidc_merge_state="+customState+"&redirect="+url.QueryEscape("/dashboard"), nil)
|
||||
res := httptest.NewRecorder()
|
||||
tp := newTestOAuth2Provider(t, oauth2.AccessTypeOffline)
|
||||
httpmw.ExtractOAuth2(tp, nil, nil)(nil).ServeHTTP(res, req)
|
||||
|
||||
found := false
|
||||
for _, cookie := range res.Result().Cookies() {
|
||||
if cookie.Name == codersdk.OAuth2StateCookie {
|
||||
require.Equal(t, cookie.Value, customState, "expected state")
|
||||
found = true
|
||||
}
|
||||
}
|
||||
require.True(t, found, "expected state cookie")
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user