dlt-connector use jwt token for authentication at backend

This commit is contained in:
einhornimmond 2024-02-20 12:47:40 +01:00
parent f0edd68619
commit 4255c1fdfa
13 changed files with 84 additions and 28 deletions

View File

@ -6,4 +6,5 @@ export const ADMIN_RIGHTS = [
RIGHTS.UNDELETE_USER,
RIGHTS.COMMUNITY_UPDATE,
RIGHTS.COMMUNITY_BY_UUID,
RIGHTS.COMMUNITY_BY_IDENTIFIER,
]

View File

@ -0,0 +1,3 @@
import { RIGHTS } from './RIGHTS'
export const DLT_CONNECTOR_RIGHTS = [RIGHTS.COMMUNITY_BY_IDENTIFIER]

View File

@ -59,5 +59,6 @@ export enum RIGHTS {
DELETE_USER = 'DELETE_USER',
UNDELETE_USER = 'UNDELETE_USER',
COMMUNITY_BY_UUID = 'COMMUNITY_BY_UUID',
COMMUNITY_BY_IDENTIFIER = 'COMMUNITY_BY_IDENTIFIER',
COMMUNITY_UPDATE = 'COMMUNITY_UPDATE',
}

View File

@ -1,6 +1,7 @@
import { RoleNames } from '@/graphql/enum/RoleNames'
import { ADMIN_RIGHTS } from './ADMIN_RIGHTS'
import { DLT_CONNECTOR_RIGHTS } from './DLT_CONNECTOR_RIGHTS'
import { INALIENABLE_RIGHTS } from './INALIENABLE_RIGHTS'
import { MODERATOR_RIGHTS } from './MODERATOR_RIGHTS'
import { Role } from './Role'
@ -20,5 +21,7 @@ export const ROLE_ADMIN = new Role(RoleNames.ADMIN, [
...ADMIN_RIGHTS,
])
export const ROLE_DLT_CONNECTOR = new Role(RoleNames.DLT_CONNECTOR_ROLE, DLT_CONNECTOR_RIGHTS)
// TODO from database
export const ROLES = [ROLE_UNAUTHORIZED, ROLE_USER, ROLE_MODERATOR, ROLE_ADMIN]

View File

@ -6,7 +6,13 @@ import { RoleNames } from '@enum/RoleNames'
import { INALIENABLE_RIGHTS } from '@/auth/INALIENABLE_RIGHTS'
import { decode, encode } from '@/auth/JWT'
import { RIGHTS } from '@/auth/RIGHTS'
import { ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN, ROLE_MODERATOR } from '@/auth/ROLES'
import {
ROLE_UNAUTHORIZED,
ROLE_USER,
ROLE_ADMIN,
ROLE_MODERATOR,
ROLE_DLT_CONNECTOR,
} from '@/auth/ROLES'
import { Context } from '@/server/context'
import { LogError } from '@/server/LogError'
@ -30,31 +36,35 @@ export const isAuthorized: AuthChecker<Context> = async ({ context }, rights) =>
// Set context gradidoID
context.gradidoID = decoded.gradidoID
// TODO - load from database dynamically & admin - maybe encode this in the token to prevent many database requests
// TODO this implementation is bullshit - two database queries cause our user identifiers are not aligned and vary between email, id and pubKey
try {
const user = await User.findOneOrFail({
where: { gradidoID: decoded.gradidoID },
withDeleted: true,
relations: ['emailContact', 'userRoles'],
})
context.user = user
context.role = ROLE_USER
if (user.userRoles?.length > 0) {
switch (user.userRoles[0].role) {
case RoleNames.ADMIN:
context.role = ROLE_ADMIN
break
case RoleNames.MODERATOR:
context.role = ROLE_MODERATOR
break
default:
context.role = ROLE_USER
if (context.gradidoID === 'dlt-connector') {
context.role = ROLE_DLT_CONNECTOR
} else {
// TODO - load from database dynamically & admin - maybe encode this in the token to prevent many database requests
// TODO this implementation is bullshit - two database queries cause our user identifiers are not aligned and vary between email, id and pubKey
try {
const user = await User.findOneOrFail({
where: { gradidoID: decoded.gradidoID },
withDeleted: true,
relations: ['emailContact', 'userRoles'],
})
context.user = user
context.role = ROLE_USER
if (user.userRoles?.length > 0) {
switch (user.userRoles[0].role) {
case RoleNames.ADMIN:
context.role = ROLE_ADMIN
break
case RoleNames.MODERATOR:
context.role = ROLE_MODERATOR
break
default:
context.role = ROLE_USER
}
}
} catch {
// in case the database query fails (user deleted)
throw new LogError('401 Unauthorized')
}
} catch {
// in case the database query fails (user deleted)
throw new LogError('401 Unauthorized')
}
// check for correct rights

View File

@ -5,6 +5,7 @@ export enum RoleNames {
USER = 'USER',
MODERATOR = 'MODERATOR',
ADMIN = 'ADMIN',
DLT_CONNECTOR_ROLE = 'DLT_CONNECTOR_ROLE',
}
registerEnumType(RoleNames, {

View File

@ -1,4 +1,4 @@
CONFIG_VERSION=v4.2023-09-12
CONFIG_VERSION=v6.2024-02-20
# SET LOG LEVEL AS NEEDED IN YOUR .ENV
# POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal
@ -22,4 +22,5 @@ TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log
DLT_CONNECTOR_PORT=6010
# Route to Backend
BACKEND_SERVER_URL=http://localhost:4000
BACKEND_SERVER_URL=http://localhost:4000
JWT_SECRET=secret123

View File

@ -1,5 +1,7 @@
CONFIG_VERSION=$DLT_CONNECTOR_CONFIG_VERSION
JWT_SECRET=$JWT_SECRET
#IOTA
IOTA_API_URL=$IOTA_API_URL
IOTA_COMMUNITY_ALIAS=$IOTA_COMMUNITY_ALIAS

View File

@ -34,6 +34,7 @@
"graphql-request": "^6.1.0",
"graphql-scalars": "^1.22.2",
"helmet": "^7.1.0",
"jose": "^5.2.2",
"log4js": "^6.7.1",
"nodemon": "^2.0.20",
"protobufjs": "^7.2.5",

View File

@ -6,6 +6,7 @@ import { CONFIG } from '@/config'
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
import { logger } from '@/logging/logger'
import { LogError } from '@/server/LogError'
import { SignJWT } from 'jose'
const communityByForeign = gql`
query ($foreign: Boolean) {
@ -73,6 +74,7 @@ export class BackendClient {
public async homeCommunityUUid(): Promise<CommunityDraft> {
logger.info('check home community on backend')
this.client.setHeader('token', await this.createJWTToken())
const { data, errors } = await this.client.rawRequest<Community>(communityByForeign, {
foreign: false,
})
@ -85,4 +87,16 @@ export class BackendClient {
communityDraft.createdAt = data.community.creationDate
return communityDraft
}
private async createJWTToken(): Promise<string> {
const secret = new TextEncoder().encode(CONFIG.JWT_SECRET)
const token = await new SignJWT({ gradidoID: 'dlt-connector', 'urn:gradido:claim': true })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setIssuer('urn:gradido:issuer')
.setAudience('urn:gradido:audience')
.setExpirationTime('1m')
.sign(secret)
return token
}
}

View File

@ -9,13 +9,14 @@ const constants = {
LOG_LEVEL: process.env.LOG_LEVEL ?? 'info',
CONFIG_VERSION: {
DEFAULT: 'DEFAULT',
EXPECTED: 'v5.2024-02-24',
EXPECTED: 'v6.2024-02-20',
CURRENT: '',
},
}
const server = {
PRODUCTION: process.env.NODE_ENV === 'production' ?? false,
JWT_SECRET: process.env.JWT_SECRET ?? 'secret123',
}
const database = {

View File

@ -10,6 +10,7 @@ import { CommunityDraft } from '@/graphql/input/CommunityDraft'
import { TransactionError } from '@/graphql/model/TransactionError'
import { CommunityLoggingView } from '@/logging/CommunityLogging.view'
import { logger } from '@/logging/logger'
import { LogError } from '@/server/LogError'
import { getDataSource } from '@/typeorm/DataSource'
import { CreateTransactionRecipeContext } from '../transaction/CreateTransationRecipe.context'
@ -22,7 +23,19 @@ export class HomeCommunityRole extends CommunityRole {
public async create(communityDraft: CommunityDraft, topic: string): Promise<void> {
super.create(communityDraft, topic)
// generate key pair for signing transactions and deriving all keys for community
const keyPair = new KeyPair(new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED ?? undefined))
let mnemonic: Mnemonic
try {
mnemonic = new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED ?? undefined)
} catch (e) {
throw new LogError(
'error creating mnemonic for home community, please fill IOTA_HOME_COMMUNITY_SEED in .env',
{
IOTA_HOME_COMMUNITY_SEED: CONFIG.IOTA_HOME_COMMUNITY_SEED,
error: e,
},
)
}
const keyPair = new KeyPair(mnemonic)
keyPair.fillInCommunityKeys(this.self)
// create auf account and gmw account

View File

@ -4281,6 +4281,11 @@ jiti@^1.19.3:
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.20.0.tgz#2d823b5852ee8963585c8dd8b7992ffc1ae83b42"
integrity sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==
jose@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/jose/-/jose-5.2.2.tgz#b91170e9ba6dbe609b0c0a86568f9a1fbe4335c0"
integrity sha512-/WByRr4jDcsKlvMd1dRJnPfS1GVO3WuKyaurJ/vvXcOaUQO8rnNObCQMlv/5uCceVQIq5Q4WLF44ohsdiTohdg==
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"