Skip to content

Commit

Permalink
feat: linkcodes and preauthsessionids should not use padding (#825)
Browse files Browse the repository at this point in the history
* feat: linkcodes and preauthsessionids should not use padding

* test: check that new linkCodes and preAuthSessionIds do not contain =

* chore: update changelog

* chore: bump patch version
  • Loading branch information
porcellus authored Oct 4, 2023
1 parent 295aefb commit 457d880
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 5 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [unreleased]
## [7.0.1] - 2023-10-04

- Remove padding from link codes and pre-auth session ids in passwordless, but keep support for old format that included padding (`=` signs)

## [7.0.0] - 2023-09-19

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" }
// }
//}

version = "7.0.0"
version = "7.0.1"


repositories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ public static ConsumeCodeResponse consumeCode(TenantIdentifierWithStorage tenant
linkCodeHash = parsedDeviceId.getLinkCode(linkCodeSalt, userInputCode).getHash();
}

if (!deviceIdHash.encode().equals(deviceIdHashFromUser)) {
if (!deviceIdHash.encode().equals(deviceIdHashFromUser.replaceAll("=", ""))) {
throw new DeviceIdHashMismatchException();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class PasswordlessDeviceIdHash {
public PasswordlessDeviceIdHash(byte[] bytes) {
// We never do anything further with the bytes, so we can just encode, store and reuse it.
// If we choose to do storage based on bytes this can change.
this.encodedValue = Base64.getUrlEncoder().encodeToString(bytes);
this.encodedValue = Base64.getUrlEncoder().encodeToString(bytes).replaceAll("=", "");
}

public PasswordlessDeviceIdHash(String encodedValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static PasswordlessLinkCode decodeString(String linkCode) throws Base64En
}

public String encode() {
return Base64.getUrlEncoder().encodeToString(bytes);
return Base64.getUrlEncoder().encodeToString(bytes).replaceAll("=", "");
}

public PasswordlessLinkCodeHash getHash() throws NoSuchAlgorithmException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,81 @@ public void testConsumeLinkCode() throws Exception {
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}

/**
* success without existing user - link code with equal signs removed
*
* @throws Exception
*/
@Test
public void testConsumeLinkCodeWithoutEqualSigns() throws Exception {
String[] args = {"../"};

TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) {
return;
}

PasswordlessStorage storage = (PasswordlessStorage) StorageLayer.getStorage(process.getProcess());

Passwordless.CreateCodeResponse createCodeResponse = Passwordless.createCode(process.getProcess(), EMAIL, null,
null, null);
assertNotNull(createCodeResponse);

assert(!createCodeResponse.deviceIdHash.contains("="));
assert(!createCodeResponse.linkCode.contains("="));

long consumeStart = System.currentTimeMillis();
Passwordless.ConsumeCodeResponse consumeCodeResponse = Passwordless.consumeCode(process.getProcess(), null,
createCodeResponse.deviceIdHash, null, createCodeResponse.linkCode);

assertNotNull(consumeCodeResponse);
checkUserWithConsumeResponse(storage, consumeCodeResponse, EMAIL, null, consumeStart);

PasswordlessDevice[] devices = storage.getDevicesByEmail(new TenantIdentifier(null, null, null), EMAIL);
assertEquals(0, devices.length);

process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}

/**
* success without existing user - link code with equal signs (padding)
*
* @throws Exception
*/
@Test
public void testConsumeLinkCodeWithEqualSigns() throws Exception {
String[] args = {"../"};

TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) {
return;
}

PasswordlessStorage storage = (PasswordlessStorage) StorageLayer.getStorage(process.getProcess());

Passwordless.CreateCodeResponse createCodeResponse = Passwordless.createCode(process.getProcess(), EMAIL, null,
null, null);
assertNotNull(createCodeResponse);

long consumeStart = System.currentTimeMillis();
Passwordless.ConsumeCodeResponse consumeCodeResponse = Passwordless.consumeCode(process.getProcess(), null,
createCodeResponse.deviceIdHash + "=", null, createCodeResponse.linkCode + "=");

assertNotNull(consumeCodeResponse);
checkUserWithConsumeResponse(storage, consumeCodeResponse, EMAIL, null, consumeStart);

PasswordlessDevice[] devices = storage.getDevicesByEmail(new TenantIdentifier(null, null, null), EMAIL);
assertEquals(0, devices.length);

process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}

/**
* Success without existing user - input code
*
Expand Down

0 comments on commit 457d880

Please sign in to comment.