Skip to content

Commit

Permalink
Merge pull request #63 from wwWallet/misc-refactor
Browse files Browse the repository at this point in the history
Miscellaneous refactorizations
  • Loading branch information
emlun authored Aug 9, 2024
2 parents 73ec958 + 2757977 commit 2aa24dd
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 152 deletions.
15 changes: 0 additions & 15 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,6 @@ app.use(cors({
// define routes and middleware here
app.use('/status', statusRouter);
app.use('/user', userController);
// app.get('/jwks', async (req, res) => {
// const users = await getAllUsers();
// if (users.err) {
// return res.status(500).send({});
// }

// const jwksPromises = users.unwrap().map(async (user) => {
// const keys = JSON.parse(user.keys);
// const w = await NaturalPersonWallet.initializeWallet(keys);
// const did = w.key.did
// return { ...w.getPublicKey(), kid: did };
// })
// const jwks = await Promise.all(jwksPromises);
// return res.send(jwks);
// })



Expand Down
2 changes: 1 addition & 1 deletion src/entities/WebauthnChallenge.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class WebauthnChallengeEntity {
type: string;

// Explicit default to workaround a bug in typeorm: https://github.com/typeorm/typeorm/issues/3076#issuecomment-703128687
@Column({ nullable: true, default: () => "NULL" })
@Column({ nullable: true, default: () => "NULL", update: false })
userHandle?: string;

@Column({ nullable: false })
Expand Down
50 changes: 7 additions & 43 deletions src/entities/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class UserEntity {
@Column({ type: "blob", nullable: false })
privateData: Buffer;

@Column({ nullable: false })
@Column({ nullable: false, update: false })
@Generated("uuid")
webauthnUserHandle: string;

Expand All @@ -82,23 +82,23 @@ class WebauthnCredentialEntity {
@ManyToOne(() => UserEntity, (user) => user.webauthnCredentials, { nullable: false })
user: UserEntity;

@Column({ nullable: false })
@Column({ nullable: false, update: false })
credentialId: Buffer;

@Column({ nullable: false })
@Column({ nullable: false, update: false })
userHandle: Buffer;

// Explicit default to workaround a bug in typeorm: https://github.com/typeorm/typeorm/issues/3076#issuecomment-703128687
@Column({ nullable: true, default: () => "NULL" })
nickname: string;

@Column({ type: "datetime", nullable: false })
@Column({ type: "datetime", nullable: false, update: false })
createTime: Date;

@Column({ type: "datetime", nullable: false })
lastUseTime: Date;

@Column({ nullable: false })
@Column({ nullable: false, update: false })
publicKeyCose: Buffer;

@Column({ nullable: false })
Expand All @@ -107,10 +107,10 @@ class WebauthnCredentialEntity {
@Column("simple-json", { nullable: false })
transports: string[];

@Column({ nullable: false })
@Column({ nullable: false, update: false })
attestationObject: Buffer;

@Column({ nullable: false })
@Column({ nullable: false, update: false })
create_clientDataJSON: Buffer;

@Column({ nullable: false })
Expand Down Expand Up @@ -193,23 +193,6 @@ async function createUser(createUser: CreateUser, isAdmin: boolean = false): Pro
}
}

async function storeKeypair(username: string, did: string, keys: Buffer): Promise<Result<{}, Error>> {
try {
const res = await AppDataSource
.createQueryBuilder()
.update(UserEntity)
.set({ keys: keys, did: did })
.where('username = :username', { username })
.execute();

return Ok({});
}
catch(e) {
console.log(e);
return Err(e);
}
}

async function getUserByDID(did: string): Promise<Result<UserEntity, GetUserErr>> {
try {
const res = await userRepository.findOne({
Expand Down Expand Up @@ -332,25 +315,6 @@ async function getAllUsers(): Promise<Result<UserEntity[], GetUserErr>> {
return Err(GetUserErr.DB_ERR)
}
}
// async function addFcmTokenByDID(did: string, newFcmToken: string) {
// try {
// const res = await AppDataSource.getRepository(UserEntity)
// .createQueryBuilder("user")
// .where("user.did = :did", { did: did })
// .getOne();
// const fcmTokens: string[] = JSON.parse(res.fcmTokens.toString());
// fcmTokens.push(newFcmToken);
// const updateRes = await AppDataSource.getRepository(UserEntity)
// .createQueryBuilder("user")
// .update({ fcmTokens: JSON.stringify(fcmTokens) })
// .where("did = :did", { did: did })
// .execute();
// }
// catch(err) {
// console.log(err);
// return Err(UpdateFcmError.DB_ERR);
// }
// }

function newWebauthnCredentialEntity(data: DeepPartial<WebauthnCredentialEntity>, manager?: EntityManager): WebauthnCredentialEntity {
const entity = (manager || webauthnCredentialRepository.manager).create(WebauthnCredentialEntity, data);
Expand Down
78 changes: 34 additions & 44 deletions src/middlewares/auth.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,68 @@
import { Request, Response, NextFunction } from "express";

import { jwtVerify } from 'jose';
import { jwtVerify, SignJWT } from 'jose';
import config from "../../config";
import { getUserByDID } from "../entities/user.entity";
import { getUserByDID, UserEntity } from "../entities/user.entity";

type AppTokenPayload = {
did: string;
}

export type AppTokenUser = {
username: string;
did: string;
}

function getCookieDictionary(cookies: any) {
const cookieList = cookies.split('; ');
let cookieDict: any = {};
for (const cookie of cookieList) {
const key = cookie.split('=')[0] as string;

const val = cookie.split('=')[1];
cookieDict[key] = val;

}
return cookieDict;
export async function createAppToken(user: UserEntity): Promise<string> {
const secret = new TextEncoder().encode(config.appSecret);
const payload: AppTokenPayload = { did: user.did };
return await new SignJWT(payload)
.setProtectedHeader({ alg: "HS256" })
.sign(secret);
}

async function verifyApptoken(jwt: string): Promise<{valid: boolean, payload: any}> {
async function verifyApptoken(jwt: string): Promise<AppTokenPayload | false> {
const secret = new TextEncoder().encode(config.appSecret);
try {
const { payload, protectedHeader } = await jwtVerify(jwt, secret);
return { valid: true, payload: payload };
return payload as AppTokenPayload;
}
catch (err) {
console.log('Signature verification failed');
return { valid: false, payload: {}}
return false;
}
}

export function AuthMiddleware(req: Request, res: Response, next: NextFunction) {
let token: string;
const authorizationHeader = req.headers.authorization;
const authorizationHeader = req.headers?.authorization;
console.log("Authorization header = ", authorizationHeader)
if (req.headers != undefined && authorizationHeader != undefined) {
if (authorizationHeader.split(' ')[0] !== 'Bearer') {
res.status(401).send();
return;
}
token = authorizationHeader.split(' ')[1];
}
else {
console.log("Unauthorized access to token: ", authorizationHeader?.split(' ')[1]);
res.status(401).send(); // Unauthorized
if (authorizationHeader?.substring(0, 7) !== 'Bearer ') {
console.log("Invalid authorization header:", authorizationHeader);
res.status(401).send();
return;
}

verifyApptoken(token).then(async ({valid, payload}) => {
if (valid === false) {
let token: string = authorizationHeader.substring(7);

verifyApptoken(token).then(async (payload) => {
if (!payload) {
console.log("Unauthorized access to ", token);
res.status(401).send(); // Unauthorized
return;
}

// success
req.user = {
username: "",
did: ""
} as AppTokenUser;
req.user.did = (payload as AppTokenUser).did;
const userRes = await getUserByDID(req.user.did);
if (userRes.err) {
res.status(401).send(); // Unauthorized
return;
const { did } = payload;
const userRes = await getUserByDID(did);
if (userRes.ok) {
req.user = {
username: userRes.val.username,
did,
};
return next();
}
const user = userRes.unwrap();
req.user.username = user.username;
req.user.did = user.did;
return next();

res.status(401).send(); // Unauthorized
return;
})
.catch(e => {
console.log("Unauthorized access to ", token);
Expand Down
12 changes: 6 additions & 6 deletions src/routers/storage.router.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import express, { Router } from "express";
import express, { Request, Response, Router } from "express";
import { getAllVerifiableCredentials, getVerifiableCredentialByCredentialIdentifier, deleteVerifiableCredential } from "../entities/VerifiableCredential.entity";
import { getAllVerifiablePresentations, getPresentationByIdentifier } from "../entities/VerifiablePresentation.entity";

Expand All @@ -14,7 +14,7 @@ storageRouter.get('/vp', getAllVerifiablePresentationsController);
storageRouter.get('/vp/:presentation_identifier', getPresentationByPresentationIdentifierController);


async function getAllVerifiableCredentialsController(req, res) {
async function getAllVerifiableCredentialsController(req: Request, res: Response) {
const holderDID = req.user.did;
console.log("Holder did", holderDID)
const vcListResult = await getAllVerifiableCredentials(holderDID);
Expand All @@ -34,7 +34,7 @@ async function getAllVerifiableCredentialsController(req, res) {

}

async function getVerifiableCredentialByCredentialIdentifierController(req, res) {
async function getVerifiableCredentialByCredentialIdentifierController(req: Request, res: Response) {
const holderDID = req.user.did;
const { credential_identifier } = req.params;
const vcFetchResult = await getVerifiableCredentialByCredentialIdentifier(holderDID, credential_identifier);
Expand All @@ -46,7 +46,7 @@ async function getVerifiableCredentialByCredentialIdentifierController(req, res)
res.status(200).send(vc);
}

async function deleteVerifiableCredentialController(req, res) {
async function deleteVerifiableCredentialController(req: Request, res: Response) {
const holderDID = req.user.did;
const { credential_identifier } = req.params;
const deleteResult = await deleteVerifiableCredential(holderDID, credential_identifier);
Expand All @@ -58,7 +58,7 @@ async function deleteVerifiableCredentialController(req, res) {



async function getAllVerifiablePresentationsController(req, res) {
async function getAllVerifiablePresentationsController(req: Request, res: Response) {
const holderDID = req.user.did;
const vpListResult = await getAllVerifiablePresentations(holderDID);
if (vpListResult.err) {
Expand All @@ -75,7 +75,7 @@ async function getAllVerifiablePresentationsController(req, res) {
res.status(200).send({ vp_list: vp_list })
}

async function getPresentationByPresentationIdentifierController(req, res) {
async function getPresentationByPresentationIdentifierController(req: Request, res: Response) {
const holderDID = req.user.did;
const { presentation_identifier } = req.params;

Expand Down
29 changes: 2 additions & 27 deletions src/routers/user.router.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import express, { Request, Response, Router } from 'express';
import { SignJWT } from 'jose';
import * as uuid from 'uuid';
import crypto from 'node:crypto';
import * as SimpleWebauthn from '@simplewebauthn/server';
Expand All @@ -9,7 +8,7 @@ import { EntityManager } from "typeorm"
import config from '../../config';
import { CreateUser, createUser, deleteUserByDID, deleteWebauthnCredential, getUserByCredentials, getUserByDID, getUserByWebauthnCredential, GetUserErr, newWebauthnCredentialEntity, privateDataEtag, updateUserByDID, UpdateUserErr, updateWebauthnCredential, updateWebauthnCredentialById, UserEntity } from '../entities/user.entity';
import { checkedUpdate, EtagUpdate, jsonParseTaggedBinary } from '../util/util';
import { AuthMiddleware } from '../middlewares/auth.middleware';
import { AuthMiddleware, createAppToken } from '../middlewares/auth.middleware';
import { ChallengeErr, createChallenge, popChallenge } from '../entities/WebauthnChallenge.entity';
import * as webauthn from '../webauthn';
import * as scrypt from "../scrypt";
Expand Down Expand Up @@ -46,13 +45,9 @@ async function initSession(user: UserEntity): Promise<{
webauthnRpId: string,
webauthnUserHandle: string,
}> {
const secret = new TextEncoder().encode(config.appSecret);
const appToken = await new SignJWT({ did: user.did })
.setProtectedHeader({ alg: "HS256" })
.sign(secret);
return {
id: user.id,
appToken,
appToken: await createAppToken(user),
did: user.did,
displayName: user.displayName || user.username,
privateData: user.privateData,
Expand Down Expand Up @@ -617,25 +612,5 @@ userController.delete('/', async (req: Request, res: Response) => {
return res.status(400).send({ result: e })
}
});
// /**
// * expect 'alg' query parameter
// */
// userController.get('/keys/public', AuthMiddleware, async (req: Request, res: Response) => {
// const did = req.user?.did;
// const algorithm = req.query["alg"] as string;
// if (did == undefined) {
// res.status(401).send({ err: 'UNAUTHORIZED' });
// return;
// }
// const alg: SigningAlgorithm = algorithm as SigningAlgorithm;
// const result = await getPublicKey(did, algorithm as SigningAlgorithm);
// if (!result) {
// res.status(500).send();
// return;
// }
// const { publicKeyJwk } = result;

// res.send({ publicKeyJwk });
// });

export default noAuthUserController;
14 changes: 0 additions & 14 deletions src/services/OpenidForCredentialIssuanceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,6 @@ export class OpenidForCredentialIssuanceService implements OpenidCredentialRecei
return { issuer_state: state.issuer_state, error: null };
}

async getAvailableSupportedCredentials(legalPersonDID: string): Promise<Array<{id: string, displayName: string}>> {
const lp = (await getLegalPersonByDID(legalPersonDID)).unwrapOr(new Error("Not found"));
if (lp instanceof Error) {
return [];
}
const issuerUrlString = lp.url;
const credentialIssuerMetadata = await axios.get(issuerUrlString + "/.well-known/openid-credential-issuer");

const options = credentialIssuerMetadata.data.credentials_supported.map((val) => {
return { id: val.id, displayName: val.display[0].name };
})
return options as Array<{id: string, displayName: string}>;
}

/**
*
* @param userDid
Expand Down
1 change: 0 additions & 1 deletion src/services/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { WalletType } from "../entities/user.entity";

export interface OpenidCredentialReceiving {

getAvailableSupportedCredentials(userDid: string, legalPersonIdentifier: string): Promise<Array<{id: string, displayName: string}>>
generateAuthorizationRequestURL(userDid: string, credentialOfferURL?: string, legalPersonIdentifier?: string): Promise<{ redirect_to?: string, preauth?: boolean, ask_for_pin?: boolean }>;

handleAuthorizationResponse(userDid: string, authorizationResponseURL: string): Promise<Result<void, IssuanceErr | WalletKeystoreRequest>>;
Expand Down
2 changes: 1 addition & 1 deletion src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Err, Ok, Result } from 'ts-results';


export function isResult<T>(a: T | Result<T, unknown>): a is Result<T, unknown> {
return "val" in a && "ok" in a && "err" in a;
return a instanceof Object && "val" in a && "ok" in a && "err" in a;
}

/**
Expand Down

0 comments on commit 2aa24dd

Please sign in to comment.