[Authezat] 2. Identification Implementation on Gateway's Side

2024. 4. 13. 00:09네트워크/Authezat 프로젝트

 

이 장은 게이트웨이 쪽에서 인증 과정을 구현한 내용을 다루고 있습니다.

node는 20.12.2 버전을 사용했습니다.

 

 


Process 1 : User Identification

 

과정 1의 구현 설명입니다.

 

 

먼저 무작위 바이트열을 생성해야 합니다.

import { randomBytes } from "crypto";

const clientSeed = randomBytes(DEFAULT_SEED_LENGTH / 2).toString("hex");

 

1 byte는 2개의 hex 문자로 치환되므로, randomBytes 인자에는 원하는 길이의 0.5배를 줘야 합니다.

 

이제 생성된 무작위 바이트열과 인증 정보를 authezat 서버에 제공하여 유효한 지 요청합니다.

    const res = await postSessionAccess.send({
        query: {
            accessedBy: accessedBy,
        },
        body: {
            userId: userId,
            userPassword: userPassword,
            clientSeed: encode(clientSeed),
        },
    });

 

그리고 응답 상태에 따라 결과를 리턴합니다.

    switch (res.status) {
        case StatusCode.OK:
            return res.json() as unknown as ServerSeed;
        case StatusCode.UNAUTHORIZED:
            return new AuthenticationError();
        case StatusCode.BAD_REQUEST:
            return new InvalidRandomByteError();
    }

    return new UnknownError();

 

 

 

 

 


Process 2 : Session Creation

 

과정 2의 구현 설명입니다.

 

 

 

서버로부터 받은 무작위 바이트 문자열(serverSeed)들과

직접 생성한 클라이언트 바이트 문자열을 조합하여 세션키를 만듭니다.

export default function makeSessionKeyAsBase64(
    clientSeed: string,
    serverSeed: string,
    initializedVector: string
): string {
    if (
        clientSeed.length !== DEFAULT_SEED_LENGTH ||
        serverSeed.length !== DEFAULT_SEED_LENGTH ||
        initializedVector.length !== DEFAULT_VECTOR_LENGTH
    ) {
        throw new Error("invalid session seed or vector given");
    }

    const cipher = createCipheriv("aes-256-gcm", clientSeed, initializedVector);
    const encrypted = Buffer.concat([
        cipher.update(serverSeed),
        cipher.final(),
        cipher.getAuthTag(),
    ]);

    return encrypted.toString("base64");
}

 

그리고 만든 세션키를 서버에 제공하여 검증해주고

    const res = await postSessionEvaluate.send({
        query: {
            evaluatedBy: evaluatedBy,
        },
        body: {
            clientSeed: encode(clientSeed),
            sessionKey: sessionKey,
        },
    });

 

응답 상태에 따라 결과를 리턴합니다.

    switch (res.status) {
        case StatusCode.OK:
            return true;
        case StatusCode.BAD_REQUEST:
            return new InvalidEvaluationRequestError();
        case StatusCode.UNPROCESSABLE_CONTENT:
            return new InvalidSessionKeyError();
        case StatusCode.REQUEST_TIMEOUT:
            return new ExpiredSessionValidationError();
    }

    return new UnknownError();

 

 

 

 

 


Process 3 : Save a Session Key

 

세션키가 유효하다면, 저장해둬서 동일한 세션이 등장했을 때 빠르게 처리하게 해주면 됩니다.

 

            Session.insertMany([
                {
                    sessionKey: sessionKey,
                },
            ]);
            res.status(200).send();

 

 

여기서 Session은 몽구스(mongoose) 컬렉션 객체입니다.

import mongoose, { Schema } from "mongoose";

...

export const Session = mongoose.model(
    "Session",
    new Schema(SessionSchema),
    "sessions"
);

 

현재는 코드 내에서 직접 영속성 객체에 접근하기 때문에, 향후 인터페이스로 빼두면 좋겠네요.