Skip to content

Commit

Permalink
Merge branch 'tim/preverified_oidc' into 'master'
Browse files Browse the repository at this point in the history
feat: Pre-verified OIDC verification

See merge request TankerHQ/sdk-react-native!115
  • Loading branch information
tux3 committed Jul 30, 2024
2 parents 1605cce + c022280 commit ca19898
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ fun Verification(json: ReadableMap): Verification {
json.getString("preverifiedPhoneNumber")?.let {
return PreverifiedPhoneNumberVerification(it)
}
json.getString("preverifiedOidcSubject")?.let {
return PreverifiedOIDCVerification(it, json.getString("oidcProviderId")!!)
}
json.getString("e2ePassphrase")?.let {
return E2ePassphraseVerification(it)
}
Expand Down
102 changes: 102 additions & 0 deletions example/src/test_verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ import {
import { createTanker, clearTankerDataDirs } from './tests';
import base64 from 'react-native-base64';

function extractOidcSubject(jwt: string): string {
const b64UrlNoPadBody = jwt.split('.')[1]!;
const b64NoPadBody = b64UrlNoPadBody
.replaceAll('-', '+')
.replaceAll('_', '/');

const remainder = b64NoPadBody.length % 4;
const paddingBytes = (4 - remainder) % 4;
const b64Body = b64NoPadBody + '='.repeat(paddingBytes);

const body = base64.decode(b64Body);
return JSON.parse(body).sub;
}

export const verifyTests = () => {
describe('Verify tests', () => {
let tanker: Tanker;
Expand Down Expand Up @@ -142,6 +156,22 @@ export const verifyTests = () => {
).is.rejectedWith(InvalidArgument);
});

it('fails to register with preverified oidc', async () => {
const oidcConfig = await getOidcConfig();
const appResponse = await setAppOidcConfig(oidcConfig);
const oidcProviderResponse = appResponse.oidc_providers[0]!!;

await tanker.start(identity);
await expect(
tanker.registerIdentity({
preverifiedOidcSubject: 'martine',
oidcProviderId: oidcProviderResponse.id,
})
).is.rejectedWith(InvalidArgument);

await setAppOidcConfig(undefined); // Cleanup
});

it('fails to verify with preverified email', async () => {
const email = '[email protected]';
await tanker.start(identity);
Expand Down Expand Up @@ -174,6 +204,35 @@ export const verifyTests = () => {
await secondDevice.stop();
});

it('fails to verify with preverified oidc', async () => {
const oidcConfig = await getOidcConfig();
const appResponse = await setAppOidcConfig(oidcConfig);
const oidcProviderResponse = appResponse.oidc_providers[0]!!;

await setAppOidcConfig(oidcConfig);
const oidcToken = await getGoogleIdToken(
oidcConfig,
oidcConfig.users.martine!!
);

await tanker.start(identity);
await tanker.setOidcTestNonce(await tanker.createOidcNonce());
await tanker.registerIdentity({ oidcIdToken: oidcToken });
await tanker.stop();

let secondDevice = await createTanker();
await secondDevice.start(identity);
await expect(
secondDevice.verifyIdentity({
preverifiedOidcSubject: 'martine',
oidcProviderId: oidcProviderResponse.id,
})
).is.rejectedWith(InvalidArgument);

await secondDevice.stop();
await setAppOidcConfig(undefined); // Cleanup
});

it('can use set verification method with preverified email', async () => {
const email = '[email protected]';
const pass = { passphrase: 'Shame, dring dring' };
Expand Down Expand Up @@ -250,6 +309,49 @@ export const verifyTests = () => {
await secondDevice.stop();
});

it('can use set verification method with preverified oidc', async () => {
const oidcConfig = await getOidcConfig();
const appResponse = await setAppOidcConfig(oidcConfig);
const oidcProviderResponse = appResponse.oidc_providers[0]!!;

await setAppOidcConfig(oidcConfig);
const oidcToken = await getGoogleIdToken(
oidcConfig,
oidcConfig.users.martine!!
);
const oidcSubject = extractOidcSubject(oidcToken);

const pass = { passphrase: 'Malotru' };
await tanker.start(identity);
await tanker.registerIdentity(pass);
await tanker.setVerificationMethod({
preverifiedOidcSubject: oidcSubject,
oidcProviderId: oidcProviderResponse.id,
});
expect(await tanker.getVerificationMethods()).to.have.deep.members([
{
type: 'passphrase',
},
{
type: 'oidcIdToken',
providerId: oidcProviderResponse.id,
providerDisplayName: oidcProviderResponse.display_name,
},
]);
await tanker.stop();

let secondDevice = await createTanker();
await secondDevice.start(identity);
await secondDevice.setOidcTestNonce(await secondDevice.createOidcNonce());
await secondDevice.verifyIdentity({
oidcIdToken: oidcToken,
});
expect(secondDevice.status).eq(Tanker.statuses.READY);

await secondDevice.stop();
await setAppOidcConfig(undefined); // Cleanup
});

it('can register an e2e passphrase', async () => {
const e2ePassphrase = 'So we all are agreed';
await tanker.start(identity);
Expand Down
3 changes: 3 additions & 0 deletions ios/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public class Utils: NSObject {
if let preverifiedPhoneNumber = dict["preverifiedPhoneNumber"] as? String {
return Verification(preverifiedPhoneNumber: preverifiedPhoneNumber)
}
if let preverifiedOIDCSubject = dict["preverifiedOidcSubject"] as? String, let providerID = dict["oidcProviderId"] as? String {
return Verification(preverifiedOIDCSubject: preverifiedOIDCSubject, providerID: providerID)
}
if let e2ePassphrase = dict["e2ePassphrase"] as? String {
return Verification(e2ePassphrase: e2ePassphrase)
}
Expand Down
22 changes: 22 additions & 0 deletions src/verification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export type PreverifiedEmailVerification = { preverifiedEmail: string };
export type PreverifiedPhoneNumberVerification = {
preverifiedPhoneNumber: string;
};
export type PreverifiedOidcVerification = {
preverifiedOidcSubject: string;
oidcProviderId: string;
};
export type E2ePassphraseVerification = { e2ePassphrase: string };

export type Verification =
Expand All @@ -61,6 +65,7 @@ export type Verification =
| PhoneNumberVerification
| PreverifiedEmailVerification
| PreverifiedPhoneNumberVerification
| PreverifiedOidcVerification
| E2ePassphraseVerification;

export type VerificationOptions = {
Expand All @@ -77,6 +82,7 @@ const validMethods = [
'phoneNumber',
'preverifiedEmail',
'preverifiedPhoneNumber',
'preverifiedOidcSubject',
'e2ePassphrase',
];
const validKeys = [
Expand Down Expand Up @@ -167,6 +173,22 @@ export const assertVerification = (verification: Verification) => {
verification.preverifiedPhoneNumber,
'verification.preverifiedPhoneNumber'
);
} else if ('preverifiedOidcSubject' in verification) {
assertNotEmptyString(
verification.preverifiedOidcSubject,
'verification.preverifiedOidcSubject'
);
if (!('oidcProviderId' in verification)) {
throw new InvalidArgument(
'verification',
'oidc pre-verification should also have a oidcProviderId',
verification
);
}
assertNotEmptyString(
verification.oidcProviderId,
'verification.oidcProviderId'
);
} else if ('e2ePassphrase' in verification) {
assertNotEmptyString(
verification.e2ePassphrase,
Expand Down

0 comments on commit ca19898

Please sign in to comment.