diff --git a/packages/openapi-to-graphql/README.md b/packages/openapi-to-graphql/README.md index 75bc961c..04af3766 100644 --- a/packages/openapi-to-graphql/README.md +++ b/packages/openapi-to-graphql/README.md @@ -315,9 +315,25 @@ The type and field names and enum values that OpenAPI-to-GraphQL generates may n ## Authentication -By default, OpenAPI-to-GraphQL will wrap API requests that need authentication in corresponding `viewers`, which allow the user to pass required credentials. OpenAPI-to-GraphQL currently supports viewers for basic authentication and API keys. For example, a query using an API key viewer is: +By default, OpenAPI-to-GraphQL will wrap API requests that need authentication in corresponding `viewers`, which allow the user to pass required credentials. OpenAPI-to-GraphQL currently supports viewers for basic authentication, bearer tokens, and API keys. For example, a query using an API key viewer is: -```javascript +```graphql +{ + viewerBasicAuth (username: "user", password: "secret") { + ... // query for authenticated data here + } +} +``` + +```graphql +{ + viewerBearerAuth (token: "bearer_token_here") { + ... // query for authenticated data here + } +} +``` + +```graphql { viewerApiKey (apiKey: "api_key_here") { ... // query for authenticated data here @@ -327,7 +343,7 @@ By default, OpenAPI-to-GraphQL will wrap API requests that need authentication i OpenAPI-to-GraphQL uses dedicated viewers for mutations. For example, a mutation using a basic authentication viewer is: -```javascript +```graphql mutation { mutationViewerBasic (username: "user", password: "secret") { ... // mutate authenticated data here @@ -337,7 +353,7 @@ mutation { OpenAPI-to-GraphQL further provides `anyAuth` viewers (for queries and mutations), which allow the user to simultaneously provide information for multiple authentication mechanisms. `anyAuth` viewers allow OpenAPI-to-GraphQL to resolve nested queries and mutations that encompass API requests with different authentication mechanisms. For example, consider the following query: -```javascript +```graphql { viewerAnyAuth ( exampleApiKeyProtocol: {apiKey: "a1p2i3k4e5y"} diff --git a/packages/openapi-to-graphql/test/authentication.test.ts b/packages/openapi-to-graphql/test/authentication.test.ts index e5b8a19a..99297637 100644 --- a/packages/openapi-to-graphql/test/authentication.test.ts +++ b/packages/openapi-to-graphql/test/authentication.test.ts @@ -54,6 +54,27 @@ test('Get patent using basic auth', () => { }) }) +test('Get patent using bearer token', () => { + const query = `{ + viewerBearerAuth(token: "master-bearer-token") { + patentWithId (patentId: "100") { + patentId + } + } + }` + return graphql(createdSchema, query, null, {}).then((result) => { + expect(result).toEqual({ + data: { + viewerBearerAuth: { + patentWithId: { + patentId: '100' + } + } + } + }) + }) +}) + test('Get patent using API key', () => { const query = `{ viewerApiKey2 (apiKey: "abcdef") { diff --git a/packages/openapi-to-graphql/test/example_api3_server.js b/packages/openapi-to-graphql/test/example_api3_server.js index ad4c9740..d3bce6b4 100644 --- a/packages/openapi-to-graphql/test/example_api3_server.js +++ b/packages/openapi-to-graphql/test/example_api3_server.js @@ -91,55 +91,75 @@ function startServer(PORT) { } const authMiddleware = (req, res, next) => { - if (req.headers.authorization) { - let encoded = req.headers.authorization.split(' ')[1] - let decoded = new Buffer(encoded, 'base64').toString('utf8').split(':') - - if (decoded.length === 2) { - let credentials = { - username: decoded[0], - password: decoded[1] - } - for (let user in Auth) { - if ( - Auth[user].username === credentials.username && - Auth[user].password === credentials.password - ) { + if ('authorization' in req.headers) { + const tokenizedAuth = req.headers.authorization.split(' ') + + if (tokenizedAuth.length == 2) { + const authType = tokenizedAuth[0] + const authValue = tokenizedAuth[1] + + if (authType == 'Basic') { + // Decode username and password + const decoded = new Buffer.from(authValue, 'base64').toString('utf8').split(':') + + if (decoded.length === 2) { + const credentials = { + username: decoded[0], + password: decoded[1] + } + + for (let user in Auth) { + if ( + Auth[user].username === credentials.username && + Auth[user].password === credentials.password + ) { + return next() + } + } + } else { + res.status(401).send({ + message: 'Basic Auth expects a single username and a single password' + }) + } + + } else if (authType == 'Bearer') { + + if (authValue == 'master-bearer-token') { return next() } } - res.status(401).send({ - message: 'Incorrect credentials' - }) - } else { - res.status(401).send({ - message: 'Basic Auth expects a single username and a single password' - }) } + } else if ('access_token' in req.headers) { for (let user in Auth) { if (Auth[user].accessToken === req.headers.access_token) { return next() } } - res.status(401).send({ - message: 'Incorrect credentials' - }) - return false + + } else if ('cookie' in req.headers) { + for (let user in Auth) { + if (Auth[user].accessToken === req.headers.cookie.split('=')[1]) { + return next() + } + } + } else if ('access_token' in req.query) { for (let user in Auth) { if (Auth[user].accessToken === req.query.access_token) { return next() } } - res.status(401).send({ - message: 'Incorrect credentials' - }) + } else { res.status(401).send({ message: 'Unknown/missing credentials' }) } + + res.status(401).send({ + message: 'Incorrect credentials' + }) } app.get('/api/authors/:authorId', (req, res) => { diff --git a/packages/openapi-to-graphql/test/example_api_server.js b/packages/openapi-to-graphql/test/example_api_server.js index ec62d6da..49d2ef21 100644 --- a/packages/openapi-to-graphql/test/example_api_server.js +++ b/packages/openapi-to-graphql/test/example_api_server.js @@ -5,6 +5,8 @@ 'use strict' +const { printLocation } = require('graphql') + let server // holds server object for shutdown /** @@ -299,65 +301,75 @@ function startServer(PORT) { } const authMiddleware = (req, res, next) => { - if (req.headers.authorization) { - const encoded = req.headers.authorization.split(' ')[1] - const decoded = new Buffer(encoded, 'base64').toString('utf8').split(':') - - if (decoded.length === 2) { - const credentials = { - username: decoded[0], - password: decoded[1] - } - for (let user in Auth) { - if ( - Auth[user].username === credentials.username && - Auth[user].password === credentials.password - ) { + if ('authorization' in req.headers) { + const tokenizedAuth = req.headers.authorization.split(' ') + + if (tokenizedAuth.length == 2) { + const authType = tokenizedAuth[0] + const authValue = tokenizedAuth[1] + + if (authType == 'Basic') { + // Decode username and password + const decoded = new Buffer.from(authValue, 'base64').toString('utf8').split(':') + + if (decoded.length === 2) { + const credentials = { + username: decoded[0], + password: decoded[1] + } + + for (let user in Auth) { + if ( + Auth[user].username === credentials.username && + Auth[user].password === credentials.password + ) { + return next() + } + } + } else { + res.status(401).send({ + message: 'Basic Auth expects a single username and a single password' + }) + } + + } else if (authType == 'Bearer') { + + if (authValue == 'master-bearer-token') { return next() } } - res.status(401).send({ - message: 'Incorrect credentials' - }) - } else { - res.status(401).send({ - message: 'Basic Auth expects a single username and a single password' - }) } + } else if ('access_token' in req.headers) { for (let user in Auth) { if (Auth[user].accessToken === req.headers.access_token) { return next() } } - res.status(401).send({ - message: 'Incorrect credentials' - }) - return false + } else if ('cookie' in req.headers) { for (let user in Auth) { if (Auth[user].accessToken === req.headers.cookie.split('=')[1]) { return next() } } - res.status(401).send({ - message: 'Incorrect credentials' - }) - return false + } else if ('access_token' in req.query) { for (let user in Auth) { if (Auth[user].accessToken === req.query.access_token) { return next() } } - res.status(401).send({ - message: 'Incorrect credentials' - }) + } else { res.status(401).send({ message: 'Unknown/missing credentials' }) } + + res.status(401).send({ + message: 'Incorrect credentials' + }) } app.get('/api/users', (req, res) => { diff --git a/packages/openapi-to-graphql/test/example_gql_server.js b/packages/openapi-to-graphql/test/example_gql_server.js index 26bdf075..decc1322 100644 --- a/packages/openapi-to-graphql/test/example_gql_server.js +++ b/packages/openapi-to-graphql/test/example_gql_server.js @@ -11,10 +11,10 @@ const { graphqlHTTP } = require('express-graphql') const app = express() const openAPIToGraphQL = require('../dist/index') -// const oas = require('./fixtures/example_oas.json') +const oas = require('./fixtures/example_oas.json') // const oas = require('./fixtures/example_oas2.json') // const oas = require('./fixtures/example_oas3.json') -const oas = require('./fixtures/example_oas4.json') +// const oas = require('./fixtures/example_oas4.json') // const oas = require('./fixtures/example_oas5.json') // const oas = require('./fixtures/example_oas6.json') // const oas = require('./fixtures/example_oas7.json') diff --git a/packages/openapi-to-graphql/test/fixtures/example_oas.json b/packages/openapi-to-graphql/test/fixtures/example_oas.json index 94cd8602..dc14a871 100644 --- a/packages/openapi-to-graphql/test/fixtures/example_oas.json +++ b/packages/openapi-to-graphql/test/fixtures/example_oas.json @@ -765,14 +765,17 @@ } }, "security": [ - { - "example_api_basic_protocol": [] - }, { "example_api_key_protocol_2": [] }, { "example_api_key_protocol_3": [] + }, + { + "example_api_basic_protocol": [] + }, + { + "example_api_bearer_protocol": [] } ] } @@ -1640,6 +1643,10 @@ "example_api_basic_protocol": { "type": "http", "scheme": "basic" + }, + "example_api_bearer_protocol": { + "type": "http", + "scheme": "bearer" } }, "parameters": {