Can't authorize in a route using #nestjs/swagger#5.0.9 because I dont know how to configure the Document` in a right way and I couldn't find a workable answer in authorization official docs / stackoverflow / github.
I've stumbled upon a problem with JWT authorization in swagger. I'm using "#nestjs/swagger": "^5.0.9" and after I'm getting my access-token from a public route, I'm inserting it into the swagger ui field 'Authorize' which is configured with .addBearerAuth() method, which in this version(5.0.9) has this signature
addBearerAuth(options?: SecuritySchemeObject, name?: string)
as opposed to it lower version.
I've tested my API in Postman and I'm easily get an authorization throw it, I've also created an intersector which is printing headers before route call, but unfortunately it only prints them while I'm calling a public route :/
I only know that Postman is setting a Bearer token and it goes throw the route, and nothing similar is happening with swagger.
I've tried a lot of combinations of this configuration, but I haven't come to a solution in result of which I'm getting authorized in my route method, from swagger I can't reach it because of the swagger auth is not setting an authorization header in case of a bad config or of me doing something completely wrong. And I can't figure it out.
Config of a addBearerAuth is placed lower:
// swagger config
...
const config = new DocumentBuilder()
.setTitle('SWAGGER API')
.setVersion('1.0.0')
.addBearerAuth(
{
// I was also testing it without prefix 'Bearer ' before the JWT
description: `[just text field] Please enter token in following format: Bearer <JWT>`,
name: 'Authorization',
bearerFormat: 'Bearer', // I`ve tested not to use this field, but the result was the same
scheme: 'Bearer',
type: 'http', // I`ve attempted type: 'apiKey' too
in: 'Header'
},
'access-token',
)
.build();
...
Sample of a route in my controller. Is matched with a #ApiBearerAuth() decorator which is talking to a swagger that that method is cant be reached without an authorization.
#Get('/some-route')
#ApiBearerAuth()
#UseGuards(JwtAuthenticationGuard)
getData(
#ReqUser() user: User,
): void {
this.logger.warn({user});
}
If I understood your question you need to specify in your controller which bearer token you are using. In your case:
// swagger config
...
const config = new DocumentBuilder()
.setTitle('SWAGGER API')
.setVersion('1.0.0')
.addBearerAuth(
{
// I was also testing it without prefix 'Bearer ' before the JWT
description: `[just text field] Please enter token in following format: Bearer <JWT>`,
name: 'Authorization',
bearerFormat: 'Bearer', // I`ve tested not to use this field, but the result was the same
scheme: 'Bearer',
type: 'http', // I`ve attempted type: 'apiKey' too
in: 'Header'
},
'access-token', // This name here is important for matching up with #ApiBearerAuth() in your controller!
)
.build();
...
and in your controller:
#Get('/some-route')
#ApiBearerAuth('access-token') //edit here
#UseGuards(JwtAuthenticationGuard)
getData(
#ReqUser() user: User,
): void {
this.logger.warn({user});
}
Related
I am developing a widget that users in my company can use to communicate with end-users through Smooch.
The widget is accessible through the web browser and the communication goes mostly through a layer developed in node. However, I was trying to send attachments directly to Smooch to reduce the load in the server.
As I understand, it is necessary to use a token with a appUser scope to avoid issues with CORS.
I create the token using the following code
app.get('/getjwt', (req, res) => {
var token = jwt.sign({ scope: 'appUser', userId: req.body.userId }, SECRET, { header: { 'alg': 'HS256', 'type': 'JWT', 'kid': '[app key ID]' } });
res.send({ jwt: token });
});
I try to use the generated token (using Postman for tests) by making a request with Authorization Bearer [my generated token] and I get the following error:
{
"error": {
"code": "invalid_auth",
"description": "Invalid JWT header. Missing key id (kid)"
}
}
I have tried changing the 'kid' value to the app ID, the API key ID, and the API key Secret and I'm always getting the same error. What am I missing? Am I supposed to pass the Key ID somewhere else?
Thank you,
Your code works fine for me, what version of jsonwebtoken are you using? In v6.0.0 the headers option was renamed to header, so if you're using 5.x or lower your code should look like this instead
var token = jwt.sign({ scope: 'appUser', userId: req.body.userId }, SECRET, { headers: { 'alg': 'HS256', 'type': 'JWT', 'kid': '[app key ID]' } });
That said, Smooch already provides a fully functional web messenger / widget that you should use instead of attempting to build your own. It provides event hooks and methods to build a fully custom UI if that's what you're trying to achieve. See https://docs.smooch.io/guide/web-messenger/ and https://www.npmjs.com/package/smooch
I am trying to connect to my AWS AppSync API using the plain Apollo Client but I am not sure how to structure the authentication header correctly.
So far I have followed the header authentication documentation here: https://www.apollographql.com/docs/react/recipes/authentication.html
And have this code, which I adapted to include the token call to the Amplify authentication service but it returns a 401 error:
const httpLink = createHttpLink({
uri: '[API end point address]/graphql'
});
const authLink = setContext((_, { headers }) => {
const token = async () => (await Auth.currentSession()).getAccessToken().getJwtToken();
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : ""
}
}
})
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
})
The only documentation I can find relating to this doesn't provide any technical instructions:
When using Amazon Cognito User Pools, you can create groups that users
belong to. This information is encoded in a JWT token that your
application sends to AWS AppSync in an authorization header when
sending GraphQL operations.
From here: https://docs.aws.amazon.com/appsync/latest/devguide/security.html
I know that token is fine because if I use the AppSync JavaScript API then it works. Is there anywhere I can go to find out how to achieve this or does someone know how?
Edit:
So far i have tried changing this line:
authorization: token ? `Bearer ${token}` : ""
The following attempts:
token
jwtToken: token
authorization: token
Authorization: token
None of these have worked either.
Disclaimer: Never tried it, but here is what I would do:
Check out the AppSync Client code here as a foundation for creating a an Authentication link for Apollo Client and the AppSync server. It looks like that code provides the scaffolding for each of the available authentication methods.
Specifically, if you are trying to use the OPENID_CONNECT method of authentication, it appears as if the JWT token does not need to be prepended by Bearer (line 156).
You can see an example of it on Github from AWS sample.
Works with AppSync but very similar.
// AppSync client instantiation
const client = new AWSAppSyncClient({
url: GRAPHQL_API_ENDPOINT_URL,
region: GRAPHQL_API_REGION,
auth: {
type: AUTH_TYPE,
// Get the currently logged in users credential.
jwtToken: async () => (await Auth.currentSession()).getAccessToken().getJwtToken(),
},
// Amplify uses Amazon IAM to authorize calls to Amazon S3. This provides the relevant IAM credentials.
complexObjectsCredentials: () => Auth.currentCredentials()
});
Link to the AWS repo
Good Day everyone,
I'm learning Laravel Passport and Vue.JS(standalone) at the same time.
I'm using Password Grant Token to authenticate the user.
I encounter this issue that the secret_key must be always hidden.
I have this Login Component in my vuejs where I need to add the client_secret as parameter to get access token. However, since VUEJS is a javascript framework. there's a way that someone can see the client_secret on the minified build file.
my question is that, is it just normal? is there a way to conceal the client_secret?
at first I don't mind the issue since I have implemented CORS on laravel where I can only select the allowedOrigins. My thinking is that it doesn't matter if they know the secret key as long as I can filter the allowedOrigins.
Here's my code in VUEJS
login(){
this.$validator.validateAll().then((result) => {
if (result) {
var data = {
client_id: 3,
client_secret: 'client-secret key',
grant_type: 'password',
username: this.inputs.email,
password: this.inputs.password
}
this.$http.post("oauth/token", data).then(response => {
this.$auth.setToken(response.body.access_token, response.body.expires_in + Date.now());
bus.$emit('reload');
this.$router.push('/');
})
}
});
}
Any advice will be appreciated.
Thanks in advance.
Laravel Passport has a built in way of allowing you to consume your own API with a Javascript application. It provides a simple middleware which you can add to your web middleware group (which you'll find in App\Http\Kernel):
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
Laravel will check to see if you have a logged in user (via the standard cookie/session guard) and if so it will generate JWT for you and store it in a cookie. It will also check for this cookie's presence and validate it when you make requests to your API so you no longer need to pass an access token.
One thing to note however, is that you will need to ensure that you continue to pass your CSRF tokens with your requests (assuming you have CSRF protection turned on). If you're using Axios with Vue, you can make sure this happens very easily with the following:
window.axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest',
};
With this approach you don't need to worry about access tokens at all or expose a client_id and secret to the client.
I faced the same problem and found an interesting solution.
You can add a custom endpoint on the backend and make the request from there.
All you have to do is to:
First, create a route in the api.php file Route::post('/login', 'AuthController#login');
Then, create the AuthController and login function associated with that route php artisan make:controller AuthController
Finally, install Guzzle, the HTTP client that will allow you to make a request from PHP composer require guzzlehttp/guzzle and make the request from the login function
public function login(Request $request)
{
$http = new \GuzzleHttp\Client;
try {
$response = $http->post('http://example.test/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 2,
'client_secret' => 'your_client_secret',
'username' => $request->username,
'password' => $request->password,
]
]);
return $response->getBody();
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
if($e->getCode() == 400)
{
return response()->json('Invalid Request, Please enter email or password.', $e->getCode());
}
else if($e->getCode() == 401)
{
return response()->json('Your credentials are incorrect. Please try again', $e->getCode());
}
return response()->json('Something went wrong on the server.', $e->getCode());
}
}
Now, the vue.js front end app juste needs to send a post request to http://example.test/login with the username and password to get back the access_token without knowing the client_secret since it is abstracted to the backend.
Here is the video that explains it and implements it really well.
And a presentation about some theory and how you can store and send the token from the vue.js app once you retrieve the token.
Hope this helps.
I am using instafeed.js like so:
var feed = new Instafeed({
get: 'user',
userId: 19191919191,
limit: 9,
accessToken: 'myaccesstokenhere',
target: 'instagram',
resolution: 'standard_resolution',
after: function() {
var el = document.getElementById('instagram');
if (el.classList)
el.classList.add('show');
else
el.className += ' ' + 'show';
}
});
but I am getting this error:
The access_token provided is invalid.
I got the access_token by https://www.instagram.com/developer I registered my application and put the Client Secret as the accessToken and I got my userID from here https://smashballoon.com/instagram-feed/find-instagram-user-id/
but its still saying The access_token provided is invalid. what am I doing wrong?
You cannot use your Client Secret in place of accessToken, as the Client Secret is used server-side as part of the OAuth process in order to get the user accessToken.
I suggest reviewing Instagram's Authentication docs here to be sure you're using the Authentication strategy that makes sense for your application.
It sounds like you're more likely to want to use their Client Side Implicit Authentication (at the bottom) to get an access token. You can even do this yourself manually to just get an accessToken for testing. Once you have the accessToken, then you can simply use that in the correct field for instafeed.js to load what you want from Instagram.
You can also just get your own accessToken by going to http://instagram.pixelunion.net/ and using the one generated there.
Seems you confused accessToken with Client Secret, check it here https://www.instagram.com/developer/authentication/
To make life easy you can try to generate one here http://instagram.pixelunion.net/
I'm trying to exchange my Dropbox oauth code for a token as per the http api documentation.
When I perform the command with curl thusly:
curl https://api.dropbox.com/1/oauth2/token \
-d code=<authorization code> \
-d grant_type=authorization_code \
-u <app key>:<app secret>
everything works fine, and I am returned my bearer token. Unfortunately, what
seems to be equivalent code written in node.js with the request module fails.
var request = require("request");
var config = require("./config.json");
request({
url: "https://api.dropboxapi.com/1/oauth2/token",
method: "POST",
auth: {
user: config.client_id,
pass: config.client_secret
},
json: {
code: config.code,
grant_type: "authorization_code"
}
}, function(err, resp, body) {
if (err) throw err;
console.log(body);
});
logs:
{ error_description: 'missing required field "grant_type"',
error: 'invalid_request' }
The docs
say that in the event of a 400 error (which this is), I have:
Bad input parameter. Error message should indicate which one and why.
Though as can be seen from the above code, the grant_type is being
specified.
Notably the docs give a second option to authenticate, though this too fails,
albeit with a different message:
Description (abridged)
Calls to /oauth2/token need to be authenticated using the apps's key and secret. These can either be passed as POST parameters (see parameters below) or via HTTP basic authentication. If basic authentication is used, the app key should be provided as the username, and the app secret should be provided as the password.
Params
code String The code acquired by directing users to /oauth2/authorize?response_type=code.
grant_type String The grant type, which must be authorization_code.
client_id String If credentials are passed in POST parameters, this parameter should be present and should be the app's key (found in the App Console).
client_secret String If credentials are passed in POST parameters, this parameter should be present and should be the app's secret.
redirect_uri String Only used to validate that it matches the original /oauth2/authorize, not used to redirect again.
My attempt at the alternate authentication procedure:
var request = require("request");
var config = require("./config.json");
request({
url: "https://api.dropboxapi.com/1/oauth2/token",
method: "POST",
json: {
code: config.code,
grant_type: "authorization_code",
client_id: config.client_id,
client_secret: config.client_secret
}
}, function(err, resp, body) {
if (err) throw err;
console.log(body);
});
logs:
{ error_description: 'No auth function available for given request',
error: 'invalid_request' }
In case the full response from dropbox for either of my two request attemps would be helpful I posted it on pastebin.
I am not including the redirect_uri as I did not use it as part of the code
flow. This is permitted as per the docs. In any case, I don't have any problems
when ommitting it in the curl command which does succeed.
Considering that my API call succeeds when sent through curl, I'm clearly doing
something wrong with my js request. What can I do to get the bearer token I
expect?
It looks like in your curl command, you're sending a form-encoded POST request (which is what OAuth uses), but in your Node.js code, you're sending a JSON-encoded request.
Try form: { ... } instead of json: { ... }.