Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(env-vars): auth environment validation #7621

Merged
merged 9 commits into from
Oct 1, 2024
1 change: 1 addition & 0 deletions packages/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@opencrvs/commons": "^1.3.0",
"app-module-path": "^2.2.0",
"dotenv": "^6.1.0",
"envalid": "^8.0.0",
"fp-ts": "^2.12.3",
"hapi-pino": "^9.0.0",
"hapi-sentry": "^3.1.0",
Expand Down
6 changes: 3 additions & 3 deletions packages/auth/src/config/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import { SENTRY_DSN } from '@auth/constants'
import { env } from '@auth/environment'
import { ServerRegisterPluginObject } from '@hapi/hapi'
import { logger } from '@opencrvs/commons'
import * as Pino from 'hapi-pino'
Expand All @@ -30,13 +30,13 @@ export default function getPlugins() {
})
}

if (SENTRY_DSN) {
if (env.SENTRY_DSN) {
plugins.push({
plugin: Sentry,
options: {
client: {
environment: process.env.DOMAIN,
dsn: SENTRY_DSN
dsn: env.SENTRY_DSN
},
catchLogErrors: true
}
Expand Down
40 changes: 0 additions & 40 deletions packages/auth/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,6 @@
*
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
export const REDIS_HOST = process.env.REDIS_HOST || 'localhost'
export const AUTH_HOST = process.env.AUTH_HOST || '0.0.0.0'
export const AUTH_PORT = process.env.AUTH_PORT || 4040
export const USER_MANAGEMENT_URL =
process.env.USER_MANAGEMENT_URL || 'http://localhost:3030/'
export const METRICS_URL = process.env.METRICS_URL || 'http://localhost:1050'
export const NOTIFICATION_SERVICE_URL =
process.env.NOTIFICATION_SERVICE_URL || 'http://localhost:2020/'
export const HOSTNAME = process.env.DOMAIN || '*'
export const COUNTRY_CONFIG_URL =
process.env.COUNTRY_CONFIG_URL || 'http://localhost:3040/'
export const LOGIN_URL = process.env.LOGIN_URL || 'http://localhost:3020/'
export const CLIENT_APP_URL =
process.env.CLIENT_APP_URL || 'http://localhost:3000/'

export const CERT_PRIVATE_KEY_PATH =
(process.env.CERT_PRIVATE_KEY_PATH as string) ||
'../../.secrets/private-key.pem'
export const CERT_PUBLIC_KEY_PATH =
(process.env.CERT_PUBLIC_KEY_PATH as string) ||
'../../.secrets/public-key.pem'
export const SENTRY_DSN = process.env.SENTRY_DSN

export const PRODUCTION = process.env.NODE_ENV === 'production'
export const QA_ENV = process.env.QA_ENV || false

export const CONFIG_TOKEN_EXPIRY_SECONDS = process.env
.CONFIG_TOKEN_EXPIRY_SECONDS
? parseInt(process.env.CONFIG_TOKEN_EXPIRY_SECONDS, 10)
: 604800 // 1 week

export const CONFIG_SMS_CODE_EXPIRY_SECONDS = process.env
.CONFIG_SMS_CODE_EXPIRY_SECONDS
? parseInt(process.env.CONFIG_SMS_CODE_EXPIRY_SECONDS, 10)
: 600

export const CONFIG_SYSTEM_TOKEN_EXPIRY_SECONDS = process.env
.CONFIG_SYSTEM_TOKEN_EXPIRY_SECONDS
? parseInt(process.env.CONFIG_SYSTEM_TOKEN_EXPIRY_SECONDS, 10)
: 600

export const WEB_USER_JWT_AUDIENCES = [
naftis marked this conversation as resolved.
Show resolved Hide resolved
'opencrvs:auth-user',
Expand Down
6 changes: 3 additions & 3 deletions packages/auth/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import * as redis from 'redis'
import { REDIS_HOST } from '@auth/constants'
import { env } from '@auth/environment'
import { promisify } from 'util'
import { logger } from '@opencrvs/commons'

Expand All @@ -29,9 +29,9 @@ export async function stop() {
}

export async function start() {
logger.info(`REDIS_HOST, ${JSON.stringify(REDIS_HOST)}`)
logger.info(`REDIS_HOST, ${JSON.stringify(env.REDIS_HOST)}`)
redisClient = redis.createClient({
host: REDIS_HOST,
host: env.REDIS_HOST,
retry_strategy: (options) => 1000
})
}
Expand Down
33 changes: 33 additions & 0 deletions packages/auth/src/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* OpenCRVS is also distributed under the terms of the Civil Registration
* & Healthcare Disclaimer located at http://opencrvs.org/license.
*
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/

import { cleanEnv, str, port, url, num, bool } from 'envalid'

export const env = cleanEnv(process.env, {
REDIS_HOST: str({ devDefault: 'localhost' }),
AUTH_HOST: str({ devDefault: '0.0.0.0' }),
AUTH_PORT: port({ devDefault: 4040 }),
naftis marked this conversation as resolved.
Show resolved Hide resolved
USER_MANAGEMENT_URL: url({ devDefault: 'http://localhost:3030/' }),
METRICS_URL: url({ devDefault: 'http://localhost:1050' }),
NOTIFICATION_SERVICE_URL: url({ devDefault: 'http://localhost:2020/' }),
DOMAIN: str({ devDefault: '*' }),
COUNTRY_CONFIG_URL: url({ devDefault: 'http://localhost:3040/' }),
LOGIN_URL: url({ devDefault: 'http://localhost:3020/' }),
CLIENT_APP_URL: url({ devDefault: 'http://localhost:3000/' }),
CERT_PRIVATE_KEY_PATH: str({ devDefault: '../../.secrets/private-key.pem' }),
CERT_PUBLIC_KEY_PATH: str({ devDefault: '../../.secrets/public-key.pem' }),
SENTRY_DSN: str({ devDefault: undefined }),
naftis marked this conversation as resolved.
Show resolved Hide resolved
QA_ENV: bool({ default: false }),

CONFIG_TOKEN_EXPIRY_SECONDS: num({ default: 604800 }), // 1 week
CONFIG_SMS_CODE_EXPIRY_SECONDS: num({ default: 600 }), // 10 minutes
CONFIG_SYSTEM_TOKEN_EXPIRY_SECONDS: num({ default: 600 }) // 10 minutes
})
29 changes: 10 additions & 19 deletions packages/auth/src/features/authenticate/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,6 @@
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import fetch from 'node-fetch'
import {
USER_MANAGEMENT_URL,
CERT_PRIVATE_KEY_PATH,
CERT_PUBLIC_KEY_PATH,
CONFIG_TOKEN_EXPIRY_SECONDS,
CONFIG_SYSTEM_TOKEN_EXPIRY_SECONDS,
PRODUCTION,
QA_ENV,
METRICS_URL
} from '@auth/constants'
import { resolve } from 'url'
import { readFileSync } from 'fs'
import { promisify } from 'util'
Expand All @@ -35,9 +25,10 @@ import { logger } from '@opencrvs/commons'
import { unauthorized } from '@hapi/boom'
import { chainW, tryCatch } from 'fp-ts/Either'
import { pipe } from 'fp-ts/function'
import { env } from '@auth/environment'

const cert = readFileSync(CERT_PRIVATE_KEY_PATH)
const publicCert = readFileSync(CERT_PUBLIC_KEY_PATH)
const cert = readFileSync(env.CERT_PRIVATE_KEY_PATH)
const publicCert = readFileSync(env.CERT_PUBLIC_KEY_PATH)

const sign = promisify<
Record<string, unknown>,
Expand Down Expand Up @@ -75,7 +66,7 @@ export async function authenticate(
username: string,
password: string
): Promise<IAuthentication> {
const url = resolve(USER_MANAGEMENT_URL, '/verifyPassword')
const url = resolve(env.USER_MANAGEMENT_URL, '/verifyPassword')

const res = await fetch(url, {
method: 'POST',
Expand All @@ -101,7 +92,7 @@ export async function authenticateSystem(
client_id: string,
client_secret: string
): Promise<ISystemAuthentication> {
const url = resolve(USER_MANAGEMENT_URL, '/verifySystem')
const url = resolve(env.USER_MANAGEMENT_URL, '/verifySystem')

const res = await fetch(url, {
method: 'POST',
Expand Down Expand Up @@ -135,8 +126,8 @@ export async function createToken(
subject: userId,
algorithm: 'RS256',
expiresIn: temporary
? CONFIG_SYSTEM_TOKEN_EXPIRY_SECONDS
: CONFIG_TOKEN_EXPIRY_SECONDS,
? env.CONFIG_SYSTEM_TOKEN_EXPIRY_SECONDS
: env.CONFIG_TOKEN_EXPIRY_SECONDS,
audience,
issuer
})
Expand Down Expand Up @@ -173,7 +164,7 @@ export async function generateAndSendVerificationCode(
mobile?: string,
email?: string
) {
const isDemoUser = scope.indexOf('demo') > -1 || QA_ENV
const isDemoUser = scope.indexOf('demo') > -1 || env.QA_ENV
logger.info(
`isDemoUser,
${JSON.stringify({
Expand All @@ -187,7 +178,7 @@ export async function generateAndSendVerificationCode(
} else {
verificationCode = await generateVerificationCode(nonce)
}
if (!PRODUCTION || QA_ENV) {
if (!env.isProd || env.QA_ENV) {
logger.info(
`Sending a verification to,
${JSON.stringify({
Expand Down Expand Up @@ -247,7 +238,7 @@ export async function postUserActionToMetrics(
userAgent: string,
practitionerId?: string
) {
const url = resolve(METRICS_URL, '/audit/events')
const url = resolve(env.METRICS_URL, '/audit/events')
const body = { action: action, practitionerId }
const authentication = 'Bearer ' + token

Expand Down
8 changes: 3 additions & 5 deletions packages/auth/src/features/invalidateToken/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import { setex } from '@auth/database'
import {
INVALID_TOKEN_NAMESPACE,
CONFIG_TOKEN_EXPIRY_SECONDS
} from '@auth/constants'
import { INVALID_TOKEN_NAMESPACE } from '@auth/constants'
import { env } from '@auth/environment'

export async function invalidateToken(token: string) {
return setex(
`${INVALID_TOKEN_NAMESPACE}:${token}`,
CONFIG_TOKEN_EXPIRY_SECONDS,
env.CONFIG_TOKEN_EXPIRY_SECONDS,
'INVALID'
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import fetch from 'node-fetch'
import { USER_MANAGEMENT_URL } from '@auth/constants'
import { env } from '@auth/environment'
import { resolve } from 'url'

export async function changePassword(
Expand All @@ -18,7 +18,7 @@ export async function changePassword(
remoteAddress: string,
userAgent: string
) {
const url = resolve(USER_MANAGEMENT_URL, '/changePassword')
const url = resolve(env.USER_MANAGEMENT_URL, '/changePassword')

const res = await fetch(url, {
method: 'POST',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
deleteRetrievalStepInformation
} from '@auth/features/retrievalSteps/verifyUser/service'
import { logger } from '@opencrvs/commons'
import { PRODUCTION } from '@auth/constants'
import { env } from '@auth/environment'
import { postUserActionToMetrics } from '@auth/features/authenticate/service'

interface IPayload {
Expand All @@ -47,7 +47,7 @@ export default async function sendUserNameHandler(
request.headers['x-real-ip'] || request.info.remoteAddress
const userAgent =
request.headers['x-real-user-agent'] || request.headers['user-agent']
if (!PRODUCTION || isDemoUser) {
if (!env.isProd || isDemoUser) {
logger.info(
`Sending a verification SMS,
${JSON.stringify({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import fetch from 'node-fetch'
import { NOTIFICATION_SERVICE_URL, JWT_ISSUER } from '@auth/constants'
import { JWT_ISSUER } from '@auth/constants'
import { env } from '@auth/environment'
import { resolve } from 'url'
import { IUserName, createToken } from '@auth/features/authenticate/service'

Expand All @@ -19,7 +20,7 @@ export async function sendUserName(
mobile?: string,
email?: string
) {
const url = resolve(NOTIFICATION_SERVICE_URL, '/retrieveUserName')
const url = resolve(env.NOTIFICATION_SERVICE_URL, '/retrieveUserName')
const res = await fetch(url, {
method: 'POST',
body: JSON.stringify({ msisdn: mobile, email, username, userFullName }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import fetch from 'node-fetch'
import { USER_MANAGEMENT_URL } from '@auth/constants'
import { env } from '@auth/environment'
import { resolve } from 'url'

export interface IVerifySecurityAnswerResponse {
Expand All @@ -22,7 +22,7 @@ export async function verifySecurityAnswer(
questionKey: string,
answer: string
): Promise<IVerifySecurityAnswerResponse> {
const url = resolve(USER_MANAGEMENT_URL, '/verifySecurityAnswer')
const url = resolve(env.USER_MANAGEMENT_URL, '/verifySecurityAnswer')

const res = await fetch(url, {
method: 'POST',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import fetch from 'node-fetch'
import { USER_MANAGEMENT_URL } from '@auth/constants'
import { env } from '@auth/environment'
import { resolve } from 'url'
import { get, set, del } from '@auth/database'
import { IUserName } from '@auth/features/authenticate/service'
Expand All @@ -23,7 +23,7 @@ export enum RetrievalSteps {
SECURITY_Q_VERIFIED = 'SECURITY_Q_VERIFIED'
}
export async function verifyUser(mobile?: string, email?: string) {
const url = resolve(USER_MANAGEMENT_URL, '/verifyUser')
const url = resolve(env.USER_MANAGEMENT_URL, '/verifyUser')

const res = await fetch(url, {
method: 'POST',
Expand Down
11 changes: 4 additions & 7 deletions packages/auth/src/features/verifyCode/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@
*/
import fetch from 'node-fetch'
import { del, get, set } from '@auth/database'
import {
CONFIG_SMS_CODE_EXPIRY_SECONDS,
JWT_ISSUER,
NOTIFICATION_SERVICE_URL
} from '@auth/constants'
import { JWT_ISSUER } from '@auth/constants'
import { env } from '@auth/environment'
import * as crypto from 'crypto'
import { resolve } from 'url'
import { IUserName, createToken } from '@auth/features/authenticate/service'
Expand Down Expand Up @@ -73,7 +70,7 @@ export async function sendVerificationCode(
notificationEvent,
userFullName
}
await fetch(resolve(NOTIFICATION_SERVICE_URL, 'authenticationCode'), {
await fetch(resolve(env.NOTIFICATION_SERVICE_URL, 'authenticationCode'), {
method: 'POST',
body: JSON.stringify(params),
headers: {
Expand Down Expand Up @@ -103,7 +100,7 @@ export async function checkVerificationCode(

const codeExpired =
(Date.now() - codeDetails.createdAt) / 1000 >=
CONFIG_SMS_CODE_EXPIRY_SECONDS
env.CONFIG_SMS_CODE_EXPIRY_SECONDS

if (code !== codeDetails.code) {
throw new Error('Auth code invalid')
Expand Down
Loading
Loading