Skip to content

Commit

Permalink
Add doc/tests for bearer viewer
Browse files Browse the repository at this point in the history
Signed-off-by: Alan Cha <[email protected]>
  • Loading branch information
Alan-Cha committed May 3, 2022
1 parent 47e2002 commit 8951518
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 69 deletions.
24 changes: 20 additions & 4 deletions packages/openapi-to-graphql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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"}
Expand Down
21 changes: 21 additions & 0 deletions packages/openapi-to-graphql/test/authentication.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down
76 changes: 48 additions & 28 deletions packages/openapi-to-graphql/test/example_api3_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
76 changes: 44 additions & 32 deletions packages/openapi-to-graphql/test/example_api_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

'use strict'

const { printLocation } = require('graphql')

let server // holds server object for shutdown

/**
Expand Down Expand Up @@ -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) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/openapi-to-graphql/test/example_gql_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
13 changes: 10 additions & 3 deletions packages/openapi-to-graphql/test/fixtures/example_oas.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": []
}
]
}
Expand Down Expand Up @@ -1640,6 +1643,10 @@
"example_api_basic_protocol": {
"type": "http",
"scheme": "basic"
},
"example_api_bearer_protocol": {
"type": "http",
"scheme": "bearer"
}
},
"parameters": {
Expand Down

0 comments on commit 8951518

Please sign in to comment.