mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
fix(auth): require a hashed poll secret for desktop handoff (#2357)
This commit is contained in:
1 parent
b82681b77a
commit
f703969e80
45 files changed
+382
-188
No files matched your search
@@ -7,6 +7,7 @@ import {createAuthHarness, createTestAccount, loginAccount} from './AuthTestUtil
|
||||
|
||||
interface HandoffInitiateResponse {
|
||||
code: string;
|
||||
poll_secret: string;
|
||||
}
|
||||
|
||||
interface HandoffInfoResponse {
|
||||
@@ -59,7 +60,8 @@ describe('Auth desktop handoff code normalization', () => {
|
||||
.expect(204)
|
||||
.execute();
|
||||
const status2 = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/status`)
|
||||
.post(`/auth/handoff/${initResp.code}/status`)
|
||||
.body({poll_secret: initResp.poll_secret})
|
||||
.execute();
|
||||
expect(status2.status).toBe('completed');
|
||||
expect(status2.token).toBeTruthy();
|
||||
|
||||
@@ -12,6 +12,7 @@ import {createAuthHarness, createTestAccount, fetchMe, loginAccount} from './Aut
|
||||
interface HandoffInitiateResponse {
|
||||
code: string;
|
||||
expires_at: string;
|
||||
poll_secret: string;
|
||||
}
|
||||
|
||||
interface HandoffInfoResponse {
|
||||
@@ -93,7 +94,8 @@ describe('Auth desktop handoff flow', () => {
|
||||
.expect(204)
|
||||
.execute();
|
||||
const completed = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/status`)
|
||||
.post(`/auth/handoff/${initResp.code}/status`)
|
||||
.body({poll_secret: initResp.poll_secret})
|
||||
.execute();
|
||||
const sessions = await createBuilder<Array<AuthSessionsResponseItem>>(harness, completed.token!)
|
||||
.get('/auth/sessions')
|
||||
@@ -116,13 +118,15 @@ describe('Auth desktop handoff flow', () => {
|
||||
expect(initResp.code).toBeTruthy();
|
||||
expect(validateHandoffCodeFormat(initResp.code)).toBe(true);
|
||||
expect(initResp.expires_at).toBeTruthy();
|
||||
expect(initResp.poll_secret).toBeTruthy();
|
||||
const info = await createBuilderWithoutAuth<HandoffInfoResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/info`)
|
||||
.execute();
|
||||
expect(info.status).toBe('pending');
|
||||
expect(info.client_info).toBeTruthy();
|
||||
const pending = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/status`)
|
||||
.post(`/auth/handoff/${initResp.code}/status`)
|
||||
.body({poll_secret: initResp.poll_secret})
|
||||
.execute();
|
||||
expect(pending.status).toBe('pending');
|
||||
await createBuilderWithoutAuth(harness)
|
||||
@@ -135,7 +139,8 @@ describe('Auth desktop handoff flow', () => {
|
||||
.expect(204)
|
||||
.execute();
|
||||
const completed = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/status`)
|
||||
.post(`/auth/handoff/${initResp.code}/status`)
|
||||
.body({poll_secret: initResp.poll_secret})
|
||||
.execute();
|
||||
expect(completed.status).toBe('completed');
|
||||
expect(completed.token).toBeTruthy();
|
||||
@@ -150,7 +155,8 @@ describe('Auth desktop handoff flow', () => {
|
||||
const handoffUser = handoffSession.json as UserMeResponse;
|
||||
expect(handoffUser.id).toBe(login.userId);
|
||||
const retrieved = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/status`)
|
||||
.post(`/auth/handoff/${initResp.code}/status`)
|
||||
.body({poll_secret: initResp.poll_secret})
|
||||
.execute();
|
||||
expect(retrieved.status).toBe('expired');
|
||||
});
|
||||
@@ -159,7 +165,11 @@ describe('Auth desktop handoff flow', () => {
|
||||
.post('/auth/handoff/initiate')
|
||||
.body(null)
|
||||
.execute();
|
||||
await createBuilderWithoutAuth(harness).delete(`/auth/handoff/${initResp.code}`).expect(204).execute();
|
||||
await createBuilderWithoutAuth(harness)
|
||||
.delete(`/auth/handoff/${initResp.code}`)
|
||||
.body({poll_secret: initResp.poll_secret})
|
||||
.expect(204)
|
||||
.execute();
|
||||
const cancelled = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/status`)
|
||||
.execute();
|
||||
@@ -184,7 +194,8 @@ describe('Auth desktop handoff flow', () => {
|
||||
.expect(204)
|
||||
.execute();
|
||||
const completed = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/status`)
|
||||
.post(`/auth/handoff/${initResp.code}/status`)
|
||||
.body({poll_secret: initResp.poll_secret})
|
||||
.execute();
|
||||
expect(completed.status).toBe('completed');
|
||||
expect(completed.token).toBeTruthy();
|
||||
@@ -233,8 +244,9 @@ describe('Auth desktop handoff flow', () => {
|
||||
.expect(204)
|
||||
.execute();
|
||||
const completed = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/status`)
|
||||
.post(`/auth/handoff/${initResp.code}/status`)
|
||||
.header('x-forwarded-for', pollerIp)
|
||||
.body({poll_secret: initResp.poll_secret})
|
||||
.execute();
|
||||
expect(completed.status).toBe('completed');
|
||||
expect(completed.user_id).toBe(login.userId);
|
||||
@@ -267,8 +279,9 @@ describe('Auth desktop handoff flow', () => {
|
||||
.expect(204)
|
||||
.execute();
|
||||
const completed = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/status`)
|
||||
.post(`/auth/handoff/${initResp.code}/status`)
|
||||
.header('x-forwarded-for', pollerIp)
|
||||
.body({poll_secret: initResp.poll_secret})
|
||||
.execute();
|
||||
expect(completed.status).toBe('completed');
|
||||
expect(completed.user_id).toBe(login.userId);
|
||||
|
||||
@@ -72,6 +72,7 @@ describe('Auth desktop handoff negative paths', () => {
|
||||
it('handles cancel for unknown handoff code gracefully', async () => {
|
||||
await createBuilderWithoutAuth(harness)
|
||||
.delete('/auth/handoff/unknown-code')
|
||||
.body({poll_secret: 'not-the-secret'})
|
||||
.expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.INVALID_HANDOFF_CODE)
|
||||
.execute();
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import {createAuthHarness, createTestAccount, loginAccount} from './AuthTestUtil
|
||||
|
||||
interface HandoffInitiateResponse {
|
||||
code: string;
|
||||
poll_secret?: string;
|
||||
}
|
||||
|
||||
interface HandoffStatusResponse {
|
||||
@@ -56,8 +57,38 @@ describe('Auth desktop handoff complete single use', () => {
|
||||
const status = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/status`)
|
||||
.execute();
|
||||
expect(status.status).toBe('completed');
|
||||
expect(status.token).toBeTruthy();
|
||||
expect(status.token).not.toBe(login.token);
|
||||
expect(status.status).toBe('pending');
|
||||
expect(status.token).toBeUndefined();
|
||||
});
|
||||
it('releases the token only to a poller presenting the correct secret', async () => {
|
||||
const account = await createTestAccount(harness);
|
||||
const login = await loginAccount(harness, account);
|
||||
const initResp = await createBuilderWithoutAuth<HandoffInitiateResponse>(harness)
|
||||
.post('/auth/handoff/initiate')
|
||||
.body(null)
|
||||
.execute();
|
||||
expect(initResp.poll_secret).toBeTruthy();
|
||||
await createBuilderWithoutAuth(harness).get(`/auth/handoff/${initResp.code}/info`).execute();
|
||||
await createBuilderWithoutAuth(harness)
|
||||
.post('/auth/handoff/complete')
|
||||
.header('Authorization', login.token)
|
||||
.body({
|
||||
code: initResp.code,
|
||||
user_id: login.userId,
|
||||
})
|
||||
.expect(204)
|
||||
.execute();
|
||||
const withoutSecret = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.get(`/auth/handoff/${initResp.code}/status`)
|
||||
.execute();
|
||||
expect(withoutSecret.status).toBe('pending');
|
||||
expect(withoutSecret.token).toBeUndefined();
|
||||
const withSecret = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
|
||||
.post(`/auth/handoff/${initResp.code}/status`)
|
||||
.body({poll_secret: initResp.poll_secret})
|
||||
.execute();
|
||||
expect(withSecret.status).toBe('completed');
|
||||
expect(withSecret.token).toBeTruthy();
|
||||
expect(withSecret.token).not.toBe(login.token);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user