Skip to content

[Feature Request] No ability to refresh authentication token #993

Closed
@bmax

Description

@bmax

I am unsure whether to call this a bug or a feature request. We're using opencypher with AWS Neptune and want to use the neo4j driver with the bolt protocol to allow us in the future to make an easier switch to neo4j enterprise. We are generating an AWS V4 Signature and passing it in as a basic auth to the driver.

Unfortunately, we can't figure out a way to gracefully handle the signature expiring (whether it's 5m or 24h). I imagine a lot of things would be involved in handling this gracefully (lost queries, reconnecting, transactions, etc), we would love for the driver to be able to handle the regeneration of the signature for us.

Thanks in advance!

  • Neo4j version: AWS Neptune OpenCypher
  • Neo4j Mode: N/A
  • Driver version: JS Driver 4.4.7
  • Operating system: Linux

Steps to reproduce

Here is the code that we've used to generate the neo4j driver w/ an AWS V4 Signature:

import neo4j, { Driver } from 'neo4j-driver'
import { HttpRequest } from '@aws-sdk/protocol-http'
import { defaultProvider } from '@aws-sdk/credential-provider-node'
import { SignatureV4 } from '@aws-sdk/signature-v4'
const { Sha256 } = require('@aws-crypto/sha256-js')
import { Credentials, LogLevel } from '@aws-sdk/types'
import { STS } from '@aws-sdk/client-sts'

const region = 'us-east-1'
const serviceName = 'neptune-db'
const host = process.env.AWS_NEPTUNE_URL
const port = 8182
const protocol = 'bolt+s'
const hostPort = host + ':' + port
const url = protocol + '://' + hostPort

const log_level = process.env.LOG as Exclude<LogLevel, 'all' | 'log' | 'off'>

async function assume(params): Promise<Credentials> {
  const sts = new STS({ region })
  const result = await sts.assumeRoleWithWebIdentity(params)
  if (!result.Credentials) {
    throw new Error('unable to assume credentials - empty credential object')
  }
  return {
    accessKeyId: String(result.Credentials.AccessKeyId),
    secretAccessKey: String(result.Credentials.SecretAccessKey),
    sessionToken: result.Credentials.SessionToken,
  }
}

async function signedHeader() {
  const req = new HttpRequest({
    method: 'GET',
    protocol: protocol,
    hostname: host,
    port: port,
    headers: {
      Host: hostPort,
    },
  })

  const signer = new SignatureV4({
    credentials: defaultProvider({ roleAssumerWithWebIdentity: assume }),
    region: region,
    service: serviceName,
    sha256: Sha256,
  })

  return signer
    .sign(req, { unsignableHeaders: new Set(['x-amz-content-sha256']) })
    .then(signedRequest => {
      const authInfo = {
        Authorization: signedRequest.headers['authorization'],
        HttpMethod: signedRequest.method,
        'X-Amz-Date': signedRequest.headers['x-amz-date'],
        Host: signedRequest.headers['Host'],
        'X-Amz-Security-Token': signedRequest.headers['x-amz-security-token'],
      }
      return JSON.stringify(authInfo)
    })
}

export async function createDriver() {
  const credentials = await signedHeader()
  let authToken = {
    scheme: 'basic',
    realm: 'realm',
    principal: 'username',
    credentials: credentials,
  }
  return neo4j.driver(url, authToken, {
    maxConnectionPoolSize: 10,
    disableLosslessIntegers: true,
    logging: {
      level: log_level,
      logger: (level, message) => console.log(message)
    },
  })
}

Expected behavior

Driver should be able to know when a token expires and have stored functionality to regenerate specific token.

Actual behavior

Driver fails to authenticate and all subsequent requests fail

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions