Skip to content

Commit d338178

Browse files
committed
fix: make device_challenge unique
1 parent 8e46505 commit d338178

File tree

5 files changed

+75
-8
lines changed

5 files changed

+75
-8
lines changed

oauth2/oauth2_device_code_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,73 @@ func TestDeviceCodeWithDefaultStrategy(t *testing.T) {
777777
require.NoError(t, err)
778778
require.Equal(t, loginFlowResp2.StatusCode, http.StatusBadRequest)
779779
})
780+
t.Run("case=cannot reuse device_challenge", func(t *testing.T) {
781+
var deviceChallenge string
782+
c, conf := newDeviceClient(t, reg)
783+
testhelpers.NewDeviceLoginConsentUI(t, reg.Config(),
784+
func(w http.ResponseWriter, r *http.Request) {
785+
userCode := r.URL.Query().Get("user_code")
786+
payload := hydra.AcceptDeviceUserCodeRequest{
787+
UserCode: &userCode,
788+
}
789+
790+
if deviceChallenge == "" {
791+
deviceChallenge = r.URL.Query().Get("device_challenge")
792+
}
793+
v, _, err := adminClient.OAuth2API.AcceptUserCodeRequest(context.Background()).
794+
DeviceChallenge(deviceChallenge).
795+
AcceptDeviceUserCodeRequest(payload).
796+
Execute()
797+
if err != nil {
798+
w.WriteHeader(http.StatusBadRequest)
799+
return
800+
}
801+
require.NoError(t, err)
802+
require.NotEmpty(t, v.RedirectTo)
803+
http.Redirect(w, r, v.RedirectTo, http.StatusFound)
804+
},
805+
acceptLoginHandler(t, c, subject, conf.Scopes, nil),
806+
acceptConsentHandler(t, c, subject, conf.Scopes, nil),
807+
)
808+
809+
resp, err := getDeviceCode(t, conf, nil)
810+
require.NoError(t, err)
811+
require.NotEmpty(t, resp.DeviceCode)
812+
require.NotEmpty(t, resp.UserCode)
813+
814+
hc := testhelpers.NewEmptyJarClient(t)
815+
loginFlowResp := acceptUserCode(t, conf, hc, resp)
816+
require.NoError(t, err)
817+
require.Contains(t, reg.Config().DeviceDoneURL(ctx).String(), loginFlowResp.Request.URL.Path, "did not end up in post device URL")
818+
require.Equal(t, loginFlowResp.Request.URL.Query().Get("client_id"), conf.ClientID)
819+
820+
require.NotNil(t, loginFlowResp)
821+
token, err := conf.DeviceAccessToken(context.Background(), resp)
822+
iat := time.Now()
823+
require.NoError(t, err)
824+
825+
introspectAccessToken(t, conf, token, subject)
826+
assertIDToken(t, token, conf, subject, nonce, iat.Add(reg.Config().GetIDTokenLifespan(ctx)))
827+
assertRefreshToken(t, token, conf, iat.Add(reg.Config().GetRefreshTokenLifespan(ctx)))
828+
829+
resp2, err := getDeviceCode(t, conf, nil)
830+
require.NoError(t, err)
831+
require.NotEmpty(t, resp2.DeviceCode)
832+
require.NotEmpty(t, resp2.UserCode)
833+
834+
payload := hydra.AcceptDeviceUserCodeRequest{
835+
UserCode: &resp2.UserCode,
836+
}
837+
838+
acceptResp, _, err := adminClient.OAuth2API.AcceptUserCodeRequest(context.Background()).
839+
DeviceChallenge(deviceChallenge).
840+
AcceptDeviceUserCodeRequest(payload).
841+
Execute()
842+
843+
loginFlowResp2, err := hc.Get(acceptResp.RedirectTo)
844+
require.NoError(t, err)
845+
require.Equal(t, http.StatusForbidden, loginFlowResp2.StatusCode)
846+
})
780847
}
781848

782849
func newDeviceClient(

persistence/sql/migrations/20241609000001000000_device_flow.cockroach.up.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS hydra_oauth2_device_auth_codes
2929
CREATE INDEX hydra_oauth2_device_auth_codes_request_id_idx ON hydra_oauth2_device_auth_codes (request_id, nid);
3030
CREATE INDEX hydra_oauth2_device_auth_codes_client_id_idx ON hydra_oauth2_device_auth_codes (client_id, nid);
3131
CREATE INDEX hydra_oauth2_device_auth_codes_challenge_id_idx ON hydra_oauth2_device_auth_codes (challenge_id);
32-
CREATE UNIQUE INDEX hydra_oauth2_device_auth_codes_user_code_signature_idx ON hydra_oauth2_device_auth_codes (user_code_signature, nid);
32+
CREATE UNIQUE INDEX hydra_oauth2_device_auth_codes_user_code_signature_idx ON hydra_oauth2_device_auth_codes (nid, user_code_signature);
3333

3434
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_challenge_id VARCHAR(255) NULL;
3535
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_code_request_id VARCHAR(255) NULL;
@@ -40,7 +40,7 @@ ALTER TABLE hydra_oauth2_flow ADD COLUMN device_was_used BOOL NULL;
4040
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_handled_at TIMESTAMP NULL;
4141
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_error TEXT NULL;
4242

43-
CREATE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge_id);
43+
CREATE UNIQUE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge_id);
4444

4545
ALTER TABLE hydra_client ADD COLUMN device_authorization_grant_id_token_lifespan BIGINT NULL DEFAULT NULL;
4646
ALTER TABLE hydra_client ADD COLUMN device_authorization_grant_access_token_lifespan BIGINT NULL DEFAULT NULL;

persistence/sql/migrations/20241609000001000000_device_flow.mysql.up.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS hydra_oauth2_device_auth_codes
2929
CREATE INDEX hydra_oauth2_device_auth_codes_request_id_idx ON hydra_oauth2_device_auth_codes (request_id, nid);
3030
CREATE INDEX hydra_oauth2_device_auth_codes_client_id_idx ON hydra_oauth2_device_auth_codes (client_id, nid);
3131
CREATE INDEX hydra_oauth2_device_auth_codes_challenge_id_idx ON hydra_oauth2_device_auth_codes (challenge_id);
32-
CREATE UNIQUE INDEX hydra_oauth2_device_auth_codes_user_code_signature_idx ON hydra_oauth2_device_auth_codes (user_code_signature, nid);
32+
CREATE UNIQUE INDEX hydra_oauth2_device_auth_codes_user_code_signature_idx ON hydra_oauth2_device_auth_codes (nid, user_code_signature);
3333

3434
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_challenge_id VARCHAR(255) NULL;
3535
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_code_request_id VARCHAR(255) NULL;
@@ -40,7 +40,7 @@ ALTER TABLE hydra_oauth2_flow ADD COLUMN device_was_used BOOL NULL;
4040
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_handled_at TIMESTAMP NULL;
4141
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_error TEXT NULL;
4242

43-
CREATE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge_id);
43+
CREATE UNIQUE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge_id);
4444

4545
ALTER TABLE hydra_client ADD COLUMN device_authorization_grant_id_token_lifespan BIGINT NULL DEFAULT NULL;
4646
ALTER TABLE hydra_client ADD COLUMN device_authorization_grant_access_token_lifespan BIGINT NULL DEFAULT NULL;

persistence/sql/migrations/20241609000001000000_device_flow.postgres.up.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS hydra_oauth2_device_auth_codes
2929
CREATE INDEX hydra_oauth2_device_auth_codes_request_id_idx ON hydra_oauth2_device_auth_codes (request_id, nid);
3030
CREATE INDEX hydra_oauth2_device_auth_codes_client_id_idx ON hydra_oauth2_device_auth_codes (client_id, nid);
3131
CREATE INDEX hydra_oauth2_device_auth_codes_challenge_id_idx ON hydra_oauth2_device_auth_codes (challenge_id);
32-
CREATE UNIQUE INDEX hydra_oauth2_device_auth_codes_user_code_signature_idx ON hydra_oauth2_device_auth_codes (user_code_signature, nid);
32+
CREATE UNIQUE INDEX hydra_oauth2_device_auth_codes_user_code_signature_idx ON hydra_oauth2_device_auth_codes (nid, user_code_signature);
3333

3434
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_challenge_id VARCHAR(255) NULL;
3535
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_code_request_id VARCHAR(255) NULL;
@@ -40,7 +40,7 @@ ALTER TABLE hydra_oauth2_flow ADD COLUMN device_was_used BOOLEAN NULL;
4040
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_handled_at TIMESTAMP NULL;
4141
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_error TEXT NULL;
4242

43-
CREATE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge_id);
43+
CREATE UNIQUE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge_id);
4444

4545
ALTER TABLE hydra_client ADD COLUMN device_authorization_grant_id_token_lifespan BIGINT NULL DEFAULT NULL;
4646
ALTER TABLE hydra_client ADD COLUMN device_authorization_grant_access_token_lifespan BIGINT NULL DEFAULT NULL;

persistence/sql/migrations/20241609000001000000_device_flow.up.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ CREATE TABLE IF NOT EXISTS hydra_oauth2_device_auth_codes
2424
CREATE INDEX hydra_oauth2_device_auth_codes_request_id_idx ON hydra_oauth2_device_auth_codes (request_id, nid);
2525
CREATE INDEX hydra_oauth2_device_auth_codes_client_id_idx ON hydra_oauth2_device_auth_codes (client_id, nid);
2626
CREATE INDEX hydra_oauth2_device_auth_codes_challenge_id_idx ON hydra_oauth2_device_auth_codes (challenge_id);
27-
CREATE UNIQUE INDEX hydra_oauth2_device_auth_codes_user_code_signature_idx ON hydra_oauth2_device_auth_codes (user_code_signature, nid);
27+
CREATE UNIQUE INDEX hydra_oauth2_device_auth_codes_user_code_signature_idx ON hydra_oauth2_device_auth_codes (nid, user_code_signature);
2828

2929
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_challenge_id VARCHAR(255) NULL;
3030
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_code_request_id VARCHAR(255) NULL;
@@ -35,7 +35,7 @@ ALTER TABLE hydra_oauth2_flow ADD COLUMN device_was_used BOOLEAN NULL;
3535
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_handled_at TIMESTAMP NULL;
3636
ALTER TABLE hydra_oauth2_flow ADD COLUMN device_error TEXT NULL;
3737

38-
CREATE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge_id);
38+
CREATE UNIQUE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge_id);
3939

4040
ALTER TABLE hydra_client ADD COLUMN device_authorization_grant_id_token_lifespan BIGINT NULL DEFAULT NULL;
4141
ALTER TABLE hydra_client ADD COLUMN device_authorization_grant_access_token_lifespan BIGINT NULL DEFAULT NULL;

0 commit comments

Comments
 (0)