Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

atlassian-connect-auth

Package Overview
Dependencies
Maintainers
0
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

atlassian-connect-auth

Helper for handling webhooks from Atlassian products

  • 4.1.0
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
0
Created
Source

atlassian-connect-auth

Known Vulnerabilities

This library implements authentication for installation requests, webhooks, and page loading from Atlassian products built with Connect.

For a deeper understanding of the concepts built into this library, please read through the Atlassian Connect documentation for the corresponding product:

Usage

import {
  AuthError,
  AuthErrorCode,
  CredentialsWithEntity,
  ExpressReqAuthDataProvider,
  InstallationType,
  verifyInstallation,
  verifyRequest,
} from 'atlassian-connect-auth'

// Consumers of this library have to provide a KeyProvider implementation that will fetch the public key from a CDN.
// Examples can be found under the `test` directory in this library.
import { GotKeyProvider } from './GotKeyProvider';

const baseUrl = 'https://your-app-base-url.com'
const asymmetricKeyProvider = new GotKeyProvider()

async function loadInstallationEntity(clientKey: string): Promise<CredentialsWithEntity<InstallationEntity>> {
  const storedEntity = await model.InstallationEntity.findOne({ where: { clientKey } })
  if (storedEntity) {
    return {
      sharedSecret: decrypt(storedEntity.encryptedSharedSecret),
      storedEntity,
    }
  }
}

const handleInstallation = async (req, res) => {
  try {
    const result = await verifyInstallation({
      baseUrl,
      asymmetricKeyProvider,
      authDataProvider: new ExpressReqAuthDataProvider(req),
      credentialsLoader: loadInstallationEntity,
    })

    const newInstallationEntity = req.body

    if (result.type === InstallationType.update) {
      const existingInstallationEntity = result.storedEntity
      await existingInstallationEntity.update(newInstallationEntity)
    } else {
      await model.InstallationEntity.create(newInstallationEntity)
    }

    res.sendStatus(201)
  } catch (error) {
    if (error instanceof AuthError) {
      console.warn(error)
      res.sendStatus(401)
    } else {
      console.error(error)
      res.sendStatus(500)
    }
  }
}

const handleAuth = async (req, res, next) => {
  try {
    const { connectJwt, storedEntity } = await verifyRequest({
      baseUrl,
      asymmetricKeyProvider,
      authDataProvider: new ExpressReqAuthDataProvider(req),
      credentialsLoader: loadInstallationEntity,
      queryStringHashType: 'context',
    })

    req.context = {
      accountId: connectJwt.context?.user?.accountId ?? connectJwt.sub,
      installationData: storedEntity
    }

    next()
  } catch (error) {
    if (error instanceof AuthError) {
      console.warn(error)
      res.sendStatus(401)
    } else {
      console.error(error)
      res.sendStatus(500)
    }
  }
}

const app = express()
  .post('/api/hooks/jira/installed', handleInstall)
  .post('/api/hooks/jira/uninstalled', handleAuth, handleUninstall)
  .post('/api/hooks/jira/project/created', handleAuth, handleProjectCreated)

Upgrading to 3.x with signed installs

Addon class was replaced with stateless function calls

Remove class instantiation and replace method calls with function calls as follows:

  • addon.auth()verifyRequest()
  • addon.install()verifyInstallation()

Also:

  • Move the baseUrl argument from the class instantiation to the function calls.
  • Remove the product argument altogether.

Callback changes

  • Replace loadCredentials with credentialsLoader.
    • The return value used to be any object with a required sharedSecret property.
    • Now you should return an object with a sharedSecret property and optionally your stored database value instoredEntity as follows:
        return {
          sharedSecret: '...',
          storedEntity: databaseInstallationData,
        }
      
  • Remove saveCredentials from the installation verification. Use the request body payload to persist the installation data. It's safe after verifying the installation request.
  • storedEntity will be returned by the verification function if a value is provided.
    • For installation updates (when the loader callback returns a stored entity), verifyInstallation() will return the loaded entity with an attribute also named storedEntity.
    • For new installations (when the loader callback does not return a stored entity), verifyInstallation() will not return the property storedEntity.

Query String Hash

Replace the argument skipQsh with queryStringHashType, which is an enum with the following values:

  • 'skip': skip QSH verification altogether. Use this in routes you had skipQsg: true.
  • 'computed': force verification using regular QSH algorithm.
  • 'context': force verification using static value context-qsh.
  • 'any': accepts both 'computed' and 'context'.

Note: Bitbucket Cloud does not currently support context-qsh as it does not have a JavaScript API that allows generating a context token.

Extracting the token from a request

Version 2.x took a request object as the first argument of the verifications functions. It expected an Express.js-like request object in order to extract the token from headers or query arguments.

Version 3.x decouples that from the web framework with the authDataProvider parameter.

  • Remove the first argument with the request object.
  • Provide an implementation of authDataProvider.
    • For Express.js, use provided ExpressReqAuthDataProvider. Example:
        verifyRequest({
          authDataProvider: new ExpressReqAuthDataProvider(req),
          ...
        })
      
    • Replace custom token extraction with customExtractToken with your implementation of AuthDataProvider.
      • Implement interface AuthDataProvider with your own token extraction.
      • Extend ExpressReqAuthDataProvider and add new ways of extracting the token from the req object. For instance:
        export class MyAuthDataProvider extends ExpressReqAuthDataProvider {
          extractConnectJwt(): string {
            // Custom query argument
            const jwt = this.req.query.customJwt as string
            if (jwt) {
              return jwt
            }
      
            // fallback to regular Connect token extraction
            return super.extractConnectJwt()
          }
        }
      

Signed installs and legacy verification

Add the authorizationMethod argument to the verification methods to define how you want installations to be verified.

  • sharedSecret: force legacy method that won't check new installations and will use the sharedSecret to verify installation updates and uninstallations.
  • publicKey: force new signed installs that use a public key to verify new installations, installation updates, and uninstallations.
  • any: accept both verification methods, meant to be used during the transition period. This is the default value.

Note: Bitbucket Cloud does not support signed installs as of 2021. You can still upgrade the library and keep it in compatibility mode (accepting legacy installs) as a preparation for a future upgrade.

Signed installations need to download a public key from the Atlassian Connect CDN. You need to provide an asymmetricKeyProvider to the verification functions.

  • Implement the KeyProvider interface with your HTTP client implementation.
  • The enum ConnectInstallKeysCdnUrl provides the base URLs for the Atlassian Connect CDN.
  • Look into ./test/keyProviderExamples for examples of implementations using Axios, Got, and Node Fetch.

Error codes

  • When checking error codes, replace string literals with values from the AuthErrorCode enum.
  • Code changes:
    • 'MISSED_TOKEN' is now AuthErrorCode.MISSING_JWT
    • 'MISSED_QSH' is now AuthErrorCode.MISSING_QSH

Upgrades in your app

  • Enabled signed installs in your app descriptor. For instance:
    apiMigrations: {
      gdpr: true,
      'context-qsh': true,
      'signed-install': true,
    },
    
  • Upgrade atlassian-jwt to 2.x, if you have a direct dependency.
    • This library depends on atlassian-jwt@2.x.
    • Replace encode() with encodeSymmetric() or encodeAsymmetric().
    • Replace decode() with decodeSymmetric() or decodeAsymmetric(). Passing the algorithm is required.

Keywords

FAQs

Package last updated on 10 Sep 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc