I'm new to swagger.
We have an API written already so I'm trying manually write the swagger.yaml
So far I have figured out how to do my /login route.. and get back a JWT in the response.
But I'm not sure what way to go next.
Is it possible to automatically plug the returned JWT into subsequent calls?
Or do I have to manually copy and paste the returned JWT?
If I have to manually do it.. then.. ehh.. how?
In the swagger editor an Authenticate button appears and I can click that and get an input box looking for the apikey...
But its not the same when viewing the swagger UI ... when I browse to localhost to see the swagger UI I don't get the authenticate button and don't have anywhere to paste the JWT text...
My swagger.yaml is as follows:
swagger: "2.0"
info:
version: 1.0.0
title: Identity Management Service
description: API to allow JWT authentication and authorisation
termsOfService: http://swagger.io/terms/
license:
name: MIT
url: http://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
host: localhost:8000
basePath: /
schemes:
- http
- https
securityDefinitions:
Bearer:
type: apiKey
name: Authorization
in: header
consumes:
- application/json
produces:
- application/json
paths:
/login:
post:
summary: User Authentication returning a JWT.
description: Authenticate a user.
parameters:
- name: credentials
in: body
description: maximum number of results to return
required: false
schema:
$ref: '#/definitions/creds'
responses:
"200":
description: will send JWT
default:
description: unexpected error
schema:
$ref: '#/definitions/Error'
/getUsers:
get:
summary: Gets list of all users
description: Authenticate a user.
security:
- Bearer: []
responses:
"200":
description: will send JWT
default:
description: unexpected error
schema:
$ref: '#/definitions/Error'
definitions:
creds:
type: object
required:
- username
- password
properties:
username:
type: string
password:
type: string
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
Obviously I'd much prefer to have it so that the response token from the /login call be stored and used in the /getUsers ...
The response from a call to /login looks like this:
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0eXBlIjoidXNlciIsInVzZXJpZCI6InBqbWVhbHkiLCJlbWFpbCI6InBqbWVhbHlAZ21haWwuY29tIiwiZmlyc3RuYW1lIjoiUEoiLCJsYXN0bmFtZSI6Ik1lYWx5Iiwib3JnIjoib3JnMSIsInRlYW1zIjpbInRlYW0xIl0sImFjbCI6WyJlbXBsb3llZSIsInRlYW1MZWFkIl0sInRva2VuVHlwZSI6IndlYkFwcFRva2VuIiwidG9rZW5WZXJzaW9uIjoiMSIsImlhdCI6MTQ2NzkxMDkyNSwiZXhwIjoxNDY3OTk3MzI1fQ.e4Trk-0kDoid5Xr9BQ5ZP_HMBN2l8_G2pn7ac2tt4uE",
"user": {
"type": "user",
"userid": "joebloggs",
"email": "joe#bloggs.com",
"firstname": "Joe",
"lastname": "Bloggs",
"org": "org1",
"teams": [
"team1"
],
"acl": [
"employee",
"teamLead"
],
"tokenType": "webAppToken",
"tokenVersion": "1",
"iat": 1467910925,
"exp": 1467997325
}
}
You can try this, it include an Authorization Header where you can save the token and it will apply to all endpoints.
#Bean
public Docket newsApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.securitySchemes(Lists.newArrayList(apiKey()))
.securityContexts(Lists.newArrayList(securityContext()))
.apiInfo(generateApiInfo());
}
#Bean
SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.any())
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Lists.newArrayList(
new SecurityReference("JWT", authorizationScopes));
}
private ApiKey apiKey() {
return new ApiKey("JWT", "Authorization", "header");
}
Related
Hi am using Swager APi with nodejs. I am new to this technology. i was facing issue in my code.
Tried to implement JWT token auth but unfortunately i was stuck and don't know how to over come this issue. i was getting 403 error. I have added my code and error below . so please let me know if anyone know it.
Swagger.yml
swagger: "2.0"
info:
version: "0.0.1"
title: Movie DB
# during dev, should point to your local machine
host: localhost:8000
# basePath prefixes all resource paths
basePath: /
#
schemes:
# tip: remove http to make production-grade
- http
- https
# format of bodies a client can send (Content-Type)
securityDefinitions:
Bearer:
type: apiKey
name: Authorization
in: header
consumes:
- application/json
- text/html
# format of the responses to the client (Accepts)
produces:
- application/json
paths:
/movies:
# binds a127 app logic to a route
x-swagger-router-controller: movies
get:
security:
- Bearer: []
x-security-scopes:
- admin
description: Returns 'Hello' to the caller
# used as the method name of the controller
operationId: index
parameters:
- name: name
in: query
description: The name of the person to whom to say hello
required: false
type: string
responses:
"200":
description: Success
schema:
# a pointer to a definition
$ref: "#/definitions/MovieListBody"
# responses may fall through to errors
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
post:
description: Creates a new movie entry
operationId: create
parameters:
- name: movie
required: true
in: body
description: a new movie details
schema:
$ref: "#/definitions/MovieBody"
responses:
"200":
description: a successfully stored movie details
schema:
$ref: "#/definitions/MovieBody"
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
/movies/{id}:
x-swagger-router-controller: movies
get:
description: get movie
operationId: show
parameters:
- name: id
required: true
in: path
description: get particular movie details
type: string
responses:
"200":
description: Sucess
schema:
$ref: "#/definitions/MovieBody"
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
put:
description: Update Movie
operationId: update
parameters:
- name: id
required: true
in: path
type: string
- name: movie
required: true
in: body
description: an updated movie details
schema:
$ref: "#/definitions/MovieBody"
responses:
"200":
description: Sucess
schema:
$ref: "#/definitions/MovieBody"
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
delete:
description: Delete Single Record
operationId: deleted
parameters:
- name: id
required: true
in: path
description: remove single record in db
type: string
responses:
"200":
description: Sucess
schema:
$ref: "#/definitions/MovieBody"
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
/login:
x-swagger-router-controller: movies
post:
description: Get Jwt Authentication Token
operationId: login
parameters:
- name: Userdetails
required: true
in: body
description: Jwt Auth token
schema:
$ref: "#/definitions/LoginBody"
responses:
"200":
description: Sucess
schema:
$ref: "#/definitions/LoginBody"
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
definitions:
MovieListBody:
required:
- movies
properties:
movies:
type: array
items:
$ref: "#/definitions/Movie"
Movie:
required:
- title
- gener
- year
properties:
title:
type: string
gener:
type: string
year:
type: integer
Login:
required:
- id
- name
- company
properties:
id:
type: integer
name:
type: string
company:
type: string
MovieBody:
required:
- movies
properties:
movies:
$ref: "#/definitions/Movie"
LoginBody:
required:
- details
properties:
details:
$ref: "#/definitions/Login"
ErrorResponse:
required:
- message
properties:
message:
type: string
Controller.js
'use strict';
var Movie = require('../models/movies')
var MongoClient = require('mongodb').MongoClient;
var jwt = require('jsonwebtoken')
const redis = require('redis');
const client = redis.createClient()
client.on('connect', function () {
console.log('Redis client connected');
});
client.on('error', function (err) {
console.log('Something went wrong ' + err);
});
var db;
module.exports = {index, create, show, update, deleted};
//Get Method:
function index(req,res,next)
{
console.log("hai")
var token = VerifyToken(req,res,next)
jwt.verify(req.token, 'secretkey', (err, authdata) => {
if (err) {
console.log(err)
}
else {
client.hgetall('products', (err, results) => {
if (results) {
res.send(results)
}
else {
db.collection('Ecommerce').find(30).toArray((err, results) => {
const ttl = 0
client.hmset('products', results, ttl)
res.send(results)
});
}
})
// db.collection('Ecommerce').find().toArray( (err, results) => {
// res.send(results)
// });
}
})
}
//Post Method:
function create(req,res,next)
{
var movie = res.json(req.body)
//res.json(movie)
db.collection('Ecommerce').save(movie, (err, result) => {
if (err) return console.log(err)
res.send("Inserted Scessfully")
})
}
//Get Particulardata
function show(req,res,next)
{
var number = parseInt(req.swagger.params.id.value)
db.collection('Ecommerce').find({ "id":number}).toArray((err, result) => {
console.log(result)
res.send(result)
})
}
//Update Method
function update(req,res,next)
{
var number = parseInt(req.swagger.params.id.value)
db.collection("Ecommerce").update({ "id": number }, { $set: { 'title': req.body.movies.title } }, (err, result) => {
res.send('user updated sucessfully');
});
}
//Delete Method
function deleted(req,res,next)
{
var number = parseInt(req.swagger.params.id.value)
db.collection('Ecommerce').deleteOne({ "id": number }, (err, result) => {
});
}
//Login Method
function login(req,res,next)
{
const user = req.body.details
jwt.sign({ user }, 'secretkey', { expiresIn: '30m' }, (err, token) => {
res.json({ token })
console.log({ token })
})
}
Facing Issue
{
"message": "unknown security handler: Bearer",
"code": "server_error",
"statusCode": 403
}
A bit old but in case it can help others, I believe your security definition is wrongly configured.
According to the Swagger documentation about Bearer authentication, you should use the following configuration:
securityDefinitions:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
I am using Swagger UI to generate api doc and want to call api in this page for grape api.
I set the security_definitions as below:
add_swagger_documentation(
hide_documentation_path: true,
doc_version: '18.0',
mount_path: '/api_doc',
add_version: true,
hide_format: true,
info: {
title: "Search API documentation"
},
security_definitions: {
ApiKeyAuth:{
type: "apiKey",
name: "X-Auth-Token",
in: "header",
description: "Requests should pass an api_key header."
}
},
security: {
ApiKeyAuth: []
}
)
But when input the value and sent the request, there is no value in request header.
Set the apikey value
calling the api
security is an array so it should be
security: [{ ApiKeyAuth: [] }]
For the Client this one is inside of client.yaml
/clients:
get:
tags:
- "Clients"
description: "List Clients The list capability"
produces:
- "application/json"
parameters:
- name: "tenantIdentifier"
in: "query"
required: true
type: "array"
items:
type: "string"
enum:
- "default"
responses:
200:
description: "successful operation"
400:
description: "Invalid status value"
security:
- basicAuth: []
post:
tags:
- "Clients"
summary: "Create client if address is enabled"
description: ""
operationId: "addClient"
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- name: "tenantIdentifier"
in: "query"
description: ""
required: true
type: "array"
items:
type: "string"
enum:
- "default"
- in: "body"
name: "body"
description: "Add what do you wnat to add "
required: true
schema:
allOf:
- $ref: '#/definitions/ClientStructure1'
- $ref: '#/definitions/ClientStructure2'
- $ref: '#/definitions/ClientStructure3'
responses:
405:
description: "Invalid input"
security:
- basicAuth: []
For the USER this is inside of user.yaml
/users:
get:
tags:
- "Users"
summary: "Retrieve list of users"
produces:
- "application/json"
parameters:
- name: "tenantIdentifier"
in: "query"
required: true
type: "array"
items:
type: "string"
enum:
- "default"
responses:
200:
description: "successful operation"
400:
description: "Invalid status value"
security:
- basicAuth: []
post:
tags:
- "Users"
summary: "Adds new application user."
description: "Note: Password information is not required (or processed). Password details at present are auto-generated and then sent to the email account given (which is why it can take a few seconds to complete)."
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- name: "tenantIdentifier"
in: "query"
description: ""
required: true
type: "array"
items:
type: "string"
enum:
- "default"
- in: "body"
name: "body"
description: "Mandatory Fields: username, firstname, lastname, email, officeId, roles, sendPasswordToEmail"
required: true
schema:
$ref: "#/definitions/StructureForCreateUSer"
responses:
400:
description: ""
404:
description: ""
security:
- basicAuth: []
You can't $ref whole paths, but you can $ref the contents of individual paths. In your example, you can use:
paths:
/clients:
$ref: clients.yaml#/~1clients
/users:
$ref: users.yaml#/~1users
clients.yaml#/~1clients means we take the clients.yaml file, then read the contents of the /clients node in that file and substitute the $ref with that contents. ~1clients is /clients with / escaped as ~1 according to JSON Pointer rules.
To simplify the references, you can remove the /clients: and /users: nodes from your clients.yaml and users.yaml files
# clients.yaml
get:
description: "List Clients The list capability"
...
post:
summary: "Create client if address is enabled"
...
and then use
paths:
/clients:
$ref: clients.yaml
/users:
$ref: users.yaml
OpenAPI 3 allows the use of the $ref keyword:
https://swagger.io/docs/specification/using-ref/
In my Swagger spec file, I want to return example responses, for that I can add examples in response. But that makes my spec file quite big and error prone. Is there a way to refer to a file containing JSON of an example object?
I tried something like below but it doesn't seem to work.
get:
tags:
- businesses
summary: Get Taxable Entities details
description: ''
operationId: getTaxableEntities
produces:
- application/json
parameters:
- name: business_id
in: path
required: true
type: integer
format: int32
- name: gstIn
in: query
required: false
type: integer
format: int32
responses:
'200':
description: Taxable Entities
schema:
type: file
default:
$ref: taxable_entity_example.json
'401':
description: You are not authorised to view this Taxable Entity
The example keyword does not support $ref, and OpenAPI 2.0 does not have a way to reference external examples.
You need OpenAPI 3.0 (openapi: 3.0.0) to be able to reference external examples. OAS3 provides the externalValue keyword for this purpose:
openapi: 3.0.2
...
responses:
'200':
description: Taxable Entities
content:
application/json:
schema:
type: object
examples:
myExample: # arbitrary name/label
externalValue: 'https://myserver.com/examples/taxable_entity_example.json'
externalValue can be an absolute or relative URL. Note that relative externalValue URLs are resolved against the API server URL (servers[*].url) and not the location of the API definition file.
Note for Swagger UI and Swagger Editor users: Currently (as of December 2019) the content of externalValue examples is not displayed. Follow this issue for updates.
You can do it with wrapComponents
Sample openapi.yaml
openapi: 3.0.1
info:
title: Swagger Petstore
description: 'This is a sample server Petstore server'
version: 1.0.0
servers:
- url: https://petstore.swagger.io/v2
- url: http://petstore.swagger.io/v2
paths:
/router/rest:
get:
summary: test
operationId: test
responses:
'200':
content:
application/json:
schema:
type: object
examples:
success:
summary: JSON example
value: Loading...
externalValue: 'example/test.json'
application/xml:
schema:
type: object
xml:
name: xml
examples:
success:
summary: XML example
value: Loading...
externalValue: 'example/test.xml'
Add a custom plugin to index.html
// Examples map
const examples = {};
// Custom plugin for the logic that happens before the response element is created
const CustomPlugin = () => {
return {
wrapComponents: {
response: (Original, { React, oas3Actions, oas3Selectors }) => (props) => {
const contentType = oas3Selectors.responseContentType(props.path, props.method)
const externalValue = props.response.getIn(['content', contentType, 'examples', props.activeExamplesKey, 'externalValue'])
// Check if externalValue field exists
if (externalValue) {
// Check if examples map already contains externalValue key
if (examples[externalValue]) {
// Set example value directly from examples map
props.response = props.response.setIn(['content', contentType, 'examples', props.activeExamplesKey, 'value'], examples[externalValue])
} else {
// Download external file
fetch(externalValue)
.then(res => res.text())
.then(data => {
// Put downloaded file content into the examples map
examples[externalValue] = data
// Simulate select another example action
oas3Actions.setActiveExamplesMember({
"name": 'fake',
"pathMethod": [props.path, props.method],
"contextType": "responses",
"contextName": props.code
})
// Reselect this example
oas3Actions.setActiveExamplesMember({
"name": props.activeExamplesKey,
"pathMethod": [props.path, props.method],
"contextType": "responses",
"contextName": props.code
})
})
.catch(e => console.error(e))
}
}
return React.createElement(Original, props)
}
}
}
}
window.onload = function() {
const ui = SwaggerUIBundle({
url: 'openapi.yaml',
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl,
// Add custom plugin
CustomPlugin
],
layout: "StandaloneLayout"
});
window.ui = ui;
};
I want this API to produce JSON in response HTTP body.
responses:
'200':
description: success
schema:
$ref: '#/definitions/Company'
'400':
description: client doesn't exist
definitions:
Company:
description: ''
type: object
properties:
id:
type: string
description: Empty when creating a record
name:
type: string
description: Not empty when creating record, or updating this field
email:
type: string
I think the response body would be:
{
"code": 200,
"company": {...company object...}
}
Is this correct? or
{
"200": {...company object...}
}
Here is the document that swagger tool generated: