I have a controller's login() method that can return either 200 OK with JWT token fitted with Session payload or 451 Unavailable due to legal reasons with JWT token fitted with AcceptTerms payload if user trying to log in needs to agree to new version of terms&conditions.
#Nest.Post("login")
#ApiOperation({ title: 'Log in the user given email and password', operationId: 'userLogin'})
#Nest.UseFilters(UnavailableLegalReasonsExceptionFilter)
#ApiResponse({ status: HttpStatus.OK, description: "User was properly logged in", type: JwtToken})
#ApiResponse({ status: 451, description: "User haven't confirmed the latest version of Terms & Conditions", type: JwtToken})
async login(#Nest.Body() body: DTOs.LoginDto): Promise<JwtToken> {
...
}
Now, I'd like to document it with Swagger. JwtToken is defined as:
export class JwtToken {
#ApiModelProperty({enum: TOKEN})
type: TOKEN;
#ApiModelProperty({description: "Number of seconds"})
expiresIn: number;
#ApiModelProperty({ description: "Token", example: "eyJhb ... wWFQ"})
accessToken: string;
}
and TOKEN:
export enum TOKEN {
AcceptTerms = "AcceptTerms",
Session = "Session"
}
But writing that login() returns just a JwtToken is confusing as in case of 200 the type is Session and in case of 451 it is AcceptTerms. The payloads differ significantly too:
export class JwtPayload {
#ApiModelProperty({enum: TOKEN, description: "Token type"})
type: TOKEN;
#ApiModelProperty()
issuedAt: number;
}
export class LoginJwtPayload extends JwtPayload {
#ApiModelProperty()
name: string;
#ApiModelProperty({description: "T&C version accepted"})
tncAccepted: number;
}
export class AcceptTermsJwtPayload extends JwtPayload {
#ApiModelProperty({format: "email"})
email: string;
}
200's token uses LoginJwtPayload payload and 451's uses AcceptTermsJwtPayload.
How to document it using Nest's SwaggerModule?
Related
I am trying to integrate a NextJS application with Okta, using the Authorization code flow with PKCE. The flow is not complete because the token request is not being performed.
This is the configuration for the provider:
import NextAuth from 'next-auth';
const oktaBaseUrl = 'https://my-okta-domain.com/oauth2/[auth-server-id]';
const clientId = '[My Client Id]';
const authorizationUrl =
oktaBaseUrl +
'/v1/authorize?response_type=code&response_mode=query&state=false';
const accessTokenUrl = oktaBaseUrl + '/v1/token';
const profileUrl = oktaBaseUrl + '/v1/userinfo';
export default NextAuth({
providers: [
{
id: 'okta',
name: 'Okta',
type: 'oauth',
version: '2.0',
protection: 'pkce',
clientId,
clientSecret: '',
accessTokenUrl,
authorizationUrl,
profileUrl,
scope: 'services',
params: {
grant_type: 'authorization_code',
},
profile(profile) {
return {
id: profile.id as string,
name: profile.name,
email: profile.email
};
}
}
],
});
Thefirst stage seems to be perfromed correctly, but when okta returns the code, I only see a message in my application showing an 403 code. It seems it is trying to get the code without perform the request to the token endpoint
Message in console:
[next-auth][error][oauth_get_access_token_error]
https://next-auth.js.org/errors#oauth_get_access_token_error { statusCode: 403, data: '' } okta y64EzO0u9ZbwqFdjJWqapXggDmC1bWx2DGQaITCpta4
[next-auth][error][oauth_callback_error]
https://next-auth.js.org/errors#oauth_callback_error { statusCode: 403, data: '' }
Is there a configuration I am missing?
I have this Facebook strategy:
facebook: {
access_token_endpoint: 'http://***.**/api/v0/auth/social',
access_type: 'offline',
client_id: '*******',
grant_type: 'authorization_code',
redirect_uri: socialLoginUrl + '/account/aanmelden',
response_type: 'code',
token_key: 'jwt',
userinfo_endpoint: 'https://****.***.**/user/profile'
},
When I try to register with Facebook I get the following form in the request:
code: **
client_id: **
redirect_uri: **
response_type: **
grant_type: **
I want to supply some data to the back-end when I register:
"attributes":{
"code":"*****",
"vendor":"Facebook"
},
How do I add this to the form that's being sent to the access_token_endpoint ?
// updated question.
Calling the loginWith function:
#click="$auth.loginWith('facebook', { params: data })"
data object:
data () {
return {
data: {
type: 'user',
attributes: {
vendor: 'Facebook',
code: '**'
}
},
}
},
Request payload in the browser:
code=**&client_id=**&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Faccount%2Faanmelden&response_type=code&grant_type=authorization_code
Response from the back-end:
I expect the data object to be available in the payload, but I'm not sure if I'm doing this correctly.
From the official docs, it looks like you can do
this.$auth.loginWith('facebook', { params: { /* additional authentication parameters */ } })
.then(() => this.$toast.success('Logged In!'))
I never used it with Facebook but it's working great with local (using JWT).
The related part of the documentation: https://auth.nuxtjs.org/api/auth#loginwithstrategyname-args
I have a #Post() route that I'm trying to get tested in swagger, but the request doesn't show any kind of data types in the UI. I have another #Post() route that is working and has stuff showing in swagger for the body, with just #Body() user: CreatedUserDto and everything is there.
Here's the one that's working:
#Post('register')
#ApiResponse({
status: 201,
description: 'Successful Registration of User Account.',
type: UserCreatedDto,
})
async registerUser(#Body() user: CreateUserDto): Promise<UserCreatedDto> {....}
and here is the one that is not. I've also tried it without the #ApiImplicitBody() with no luck.
#ApiImplicitBody({
name: 'user',
type: LoginUserDto,
})
#ApiResponse({
status: 200,
description: 'Successful Login!',
type: LoginResponse,
})
#ApiResponse({ status: 400, description: 'Invalid login attempt' })
#UseGuards(AuthGuard('local'))
#Post('login')
async login(
#Request() req,
#Body('user') user: LoginUserDto,
): Promise<LoginResponse> {
try {
return await this.authService.login(req.user);
} catch (e) {
throw e;
}
}
and this is the LoginUserDto
export class LoginUserDto {
#ApiModelProperty()
readonly userName: string;
#ApiModelProperty()
readonly password: string;
}
I need to describe the routes of my application, thus I use swagger:
paths:
/users:
get:
description: Returns all users
responses:
"204":
description: No Content Found
schema:
$ref: "#/definitions/NoContentFound"
definitions:
NoContentFound:
required:
- message
properties:
message:
type: string
I got this error on swagger UI:
Unknown response type
On app.js:
const User = require('../server/models/user');
const sendJsonResponse = function sendJsonResponse(req, res, status, content) {
res.status(status);
res.json(content);
};
app.get('/users/:usersid', (req, res) => {
User
.query()
.where('id', id)
.then(users => {
if(users.length === 0) {
sendJsonResponse(req, res, 204, 'No users Found');
}
});
};
How can I fix that on swagger?
Some responses, such as 204 No Content, have No Body. To indicate the response body is empty, do not specify content for the response:
Reference: Empty Response Body
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");
}