I have front-end server on nuxt.js and backend in django with django-rest-framework.
Can anyone give me example of refreshing jwt token with nuxt-auth local strategy?
I was tryed save token in vuex store, but this code return undefined
var dr = await this.$auth
.loginWith('local', {
data: {
username: this.username,
password: this.password
}
})
.then(response => {
console.log(response)
})
.catch(e => {
this.error = e + ''
})
You can use custom strategy for save refreshToken: https://auth.nuxtjs.org/reference/schemes
auth: {
strategies: {
local: { _scheme: '~/app/myCustomLocalStrategy.js', /* ... */ }
}
}
I think the approach will vary depending on what you’re doing on the server. The best advice I can offer is to grab the local strategy that exists in nuxt-auth and create a custom strategy based on it.
Nuxt-auth seems to have some hooks into setting and getting refresh tokens but they aren’t documented or particularly well integrated: there’s no hook to a refresh end point for example.
It’s not ideal, but coming up with something workable is possible. For example, you can hook into the user endpoint in your custom strategy and reauthenticate using the refresh token if the user endpoint throws a 401.
You can the local strategy where you can define your endpoint to refresh the authorization token.
Then you declare it as defined here:
refresh_token: {
prefix: '_refresh_token.'
},
Related
I want to control access to a public AWS API, meaning only my website and mobile app can consume this API, otherwise the API will reject the requests.
My approach is to secure the API using AWS Cognito with OAuth2 scopes, see more details here. The website won't need user registration, so I use cognito just to secure the API.
So far, I'm able to go to Hosted UI under App client settings in AWS Cognito. Click the link there to login, and get a "code": https://example.com/?code=f27b2de0-1111-1111-1111-11111111. And then follow How to use the code returned from Cognito to get AWS credentials? to get id_token. And finally send a HTTP request with header key=Authorization and value=<id_token>. This works for me. I see that the API rejects requests when no valid token, and returns expected results when valid token is present.
However, I have some questions:
The token expires in 30 days(I configured it to be so), and my website is a reactJS app, how can the website refresh the token? My website should not care any login here, it just need to send a valid ID Token as the Authorization header to access the API. Users should not see any login page. Should I use Cognito SDK amazon-cognito-identity-js in my react app to fetch the ID token?
Do I need setup callback url here? All I need is ensure that the API will reject the request if token is missing or invalid, I don't know what's the purpose of having callback url in this case.
Feel free to point out any other mistakes I made here.
Sample code
Here are my CDK code to setup API + Cognito.
import * as CDK from "aws-cdk-lib";
import * as CertificateManager from "aws-cdk-lib/aws-certificatemanager";
import * as Route53 from "aws-cdk-lib/aws-route53";
import * as Route53Targets from "aws-cdk-lib/aws-route53-targets";
import * as ApiGateway from "aws-cdk-lib/aws-apigateway";
import * as ELBv2 from "aws-cdk-lib/aws-elasticloadbalancingv2";
import { Construct } from "constructs";
import { StageInfo } from "../config/stage-config";
import * as Cognito from "aws-cdk-lib/aws-cognito";
export interface ApigatewayStackProps extends CDK.StackProps {
readonly packageName: string;
readonly stageInfo: StageInfo;
}
export class ApigatewayStack extends CDK.Stack {
// Prefix for CDK constrcut ID
private readonly constructIdPrefix: string;
private readonly pandaApiCognitoUserPool: Cognito.UserPool;
private readonly domainCertificate: CertificateManager.Certificate;
private readonly apiAuthorizer: ApiGateway.CfnAuthorizer;
private readonly pandaApi: ApiGateway.RestApi;
constructor(scope: Construct, id: string, props: ApigatewayStackProps) {
super(scope, id, props);
this.constructIdPrefix = `${props.packageName}-${props.stageInfo.stageName}`;
const hostedZone: Route53.IHostedZone = Route53.HostedZone.fromLookup(
this,
`${this.constructIdPrefix}-HostedZoneLookup`,
{
domainName: props.stageInfo.domainName,
}
);
this.domainCertificate = new CertificateManager.Certificate(
this,
`${this.constructIdPrefix}-pandaApiCertificate`,
{
domainName: props.stageInfo.domainName,
validation:
CertificateManager.CertificateValidation.fromDns(hostedZone),
}
);
this.pandaApi = new ApiGateway.RestApi(
this,
`${this.constructIdPrefix}-pandaApi`,
{
description: "The centralized API for panda.com",
domainName: {
domainName: props.stageInfo.domainName,
certificate: this.domainCertificate,
//mappingKey: props.pipelineStageInfo.stageName
},
defaultCorsPreflightOptions: {
allowOrigins: ApiGateway.Cors.ALL_ORIGINS,
allowMethods: [...ApiGateway.Cors.DEFAULT_HEADERS],
},
}
);
new Route53.ARecord(this, "AliasRecord", {
zone: hostedZone,
target: Route53.RecordTarget.fromAlias(
new Route53Targets.ApiGateway(this.pandaApi)
),
// or - route53.RecordTarget.fromAlias(new alias.ApiGatewayDomain(domainName)),
});
this.pandaApiCognitoUserPool = new Cognito.UserPool(this, "UserPool", {
userPoolName: `pandaApiUserPool`,
selfSignUpEnabled: false,
});
this.apiAuthorizer = new ApiGateway.CfnAuthorizer(
this,
`${this.constructIdPrefix}-pandaApiAuthorizer`,
{
name: "pandaApiAuthorizer",
type: ApiGateway.AuthorizationType.COGNITO,
identitySource: "method.request.header.Authorization",
restApiId: this.pandaApi.restApiId,
providerArns: [this.pandaApiCognitoUserPool.userPoolArn],
}
);
this.addCognitoAuthentication(props);
}
private addCognitoAuthentication(props: ApigatewayStackProps) {
this.pandaApiCognitoUserPool.addDomain("DomainName", {
cognitoDomain: {
domainPrefix: `panda-api-user-pool-${props.stageInfo.stageName.toLocaleLowerCase()}`,
},
});
this.pandaApiCognitoUserPool.addClient(
`${this.constructIdPrefix}-pandaApiUserPoolClient`,
{
userPoolClientName: `pandaApiUserPoolClient`,
generateSecret: true,
oAuth: {
flows: {
// It's highly recommend to use only the Authorization code grant flow.
// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-idp-settings.html
authorizationCodeGrant: true,
},
scopes: [Cognito.OAuthScope.OPENID],
//callbackUrls: [props.stageInfo.domainName + '/callback']
},
authFlows: {
userPassword: true,
},
refreshTokenValidity: CDK.Duration.days(30),
}
);
}
}
Cognito is for logging your users in and providing you an access token and id token as a result. Your users must log in. If they don't log in then you can't use cognito to secure you API. Further if you don't require log in, then anyone in the world can call your API.
There is no way to ensure only your website and your mobile app can talk to your API, this isn't how APIs work. APIs are available to the whole world, they are all public, you can't block requests to them, unless your users individually authenticate.
The reason for this is that any one can spoof your website or mobile application, and because of that no security mechanisms exist that would enable restricting access.
So either your API is world-writable/readable or you require your users to log in. The good news is there are tons of ways to easily do this, now that logs of AuthN providers support webauthn/fido2/passkeys out of the box.
you can probably google around for a list of providers that offer exactly what you need, but a starter list is something like this one on how to pick the best auth provider.
** I'm doing as following, I already created a custom rule.**
componentDidMount() {
console.log(token)
let response = fetch('https://DOmain.eu.auth0.com/userinfo', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + token,
},
}).then((response) => response.json())
.then(responseJson => data = responseJson).then(console.log(data.nickname));
const metadata = data["https://Domain.eu.auth0.com/user_metadata"]
console.log(metadata);
}
My rule:
The Rule you have setup looks good, but will not work as the namespace is an Auth0 domain
Any non-Auth0 HTTP or HTTPS URL can be used as a namespace identifier,
and any number of namespaces can be used
Give it a shot with an alternate namespace, example 'https://myapp.example.com/', and you should be good to go!
As a side note, I would try to avoid adding all the usermetadata to the idtoken which can cause the generated token to be too large. You should also ensure that the data being included is not sensitive and can be disclosed. Some items that may be helpful, a quick read here: https://auth0.com/docs/metadata and here: https://auth0.com/docs/scopes/current/custom-claims to help you along the way!
Currently utilizing a JWT authentication schema where the tokens have "Bearer: in the schema. Is it possible to remove the "Bearer" prefix so I wouldn't need to add it on the client side just to parse it out on the backend again? Is there a way to do this without implementing a custom scheme (So while still using the Bearer scheme) AND without having to parse the actual token for the "Bearer: " text?
Right now, the code looks like:
var token = req.headers.authorization;
var newToken = token.replace("Bearer ", "");
jwt.verify(newToken, jwtSecret, function (err, success) {
if (err) {
return res.
status(401).
end('Unauthorized, invalidtoken');
} else {
return next();
}
})
Ideally it would be implemented as such:
var token = req.headers.authorization;
jwt.verify(token, jwtSecret, function (err, success) {
if (err) {
return res.
status(401).
end('Unauthorized, invalidtoken');
} else {
return next();
}
})
Would this be okay? What are the implications of removing "Bearer" from the jwt authorization headers ?
Thanks
There is no programmatic difference from removing Bearer token in formatting the request header. If you do choose to do so, you are violating RFC and HTTP standards. It would be like sending a payload in a GET response and saving data to the database.
Use of bearer tokens derived from the Oauth design so have a look at here for standards.
I have a mobile front-end that already has facebook authetication working. I have a Sails REST API that stores user data, posts etc.. I want to add security where facebook users can only POST GET DELETE PUT their own data.
I've read a almost every tutorial for facebook authenticating a web-app, but haven't found many for authenticating with a mobile app to protect the user data. I've tried to get Passport-Facebook-Token working but I just don't understand the little documentation available. I'm coming from a objective-C background so in the node learning curve now.
Here's the link to what I'm working with but I'm obviously missing something: https://github.com/drudge/passport-facebook-token
I have:
AuthController.js
module.exports = {
facebook: function(req, res) {
passport.authenticate('facebook-token', function(error, user, info) {
// do stuff with user
res.ok();
})(req, res);
}
};
api/services/protocols/passport.js
(with some other stuff from default passport sails-generate-auth)
var FacebookTokenStrategy = require('passport-facebook-token');
passport.use('facebook-token', new FacebookTokenStrategy({
clientID : "<my_id>",
clientSecret : "<my_secret>"
},
function(accessToken, refreshToken, profile, done) {
// console.log(profile);
var user = {
'email': profile.emails[0].value,
'name' : profile.name.givenName + ' ' + profile.name.familyName,
'id' : profile.id,
'token': accessToken
}
// You can perform any necessary actions with your user at this point,
// e.g. internal verification against a users table,
// creating new user entries, etc.
return done(null, user); // the user object we just made gets passed to the route's controller as `req.user`
}
));
Do I have to do something with config/routes to make sure it only allows users with access_tokens? I just can't find any resources out there. Passport doesn't even list Passport-Facebook-Token strategy as an option on their site.
thank you for the help
I'm in the process of building a new AngularJS frontend for a Drupal 7 website. This is using the Services module with session-based authentication, across two domains using CORS. I am able to authenticate with Drupal, retrieve the user object and session data, and then get the CSRF token from the services module. What I'm having trouble with is setting all this up in the header so that subsequent requests are authenticated. I understand the overall concept but am new to both AngularJS and preventing CSRF attacks.
From what I have gathered reading about this set-up with AngularJS and RubyOnRails, there can be inconsistencies between platforms concerning what the token is named and how it is processed. There also seems to be a number of suggestions on how to set this token in the header. However, I'm having trouble in finding a solid example of how to get these platforms speaking the same language.
The only thing I'm doing with my $httpProvider in app.js is:
delete $httpProvider.defaults.headers.common['X-Requested-With'];
The login controller, in controller.js:
.controller('LoginCtrl', ['$scope', '$http', '$cookies', 'SessionService', function($scope, $http, $cookies, SessionService) {
$scope.login = function(user) {
//set login url and variables
var url = 'http://mywebsite.com/service/default/user/login.json';
var postDataString = 'name=' + encodeURIComponent(user.username) + '&pass=' + encodeURIComponent(user.password);
$http({
method: 'POST',
url: url,
data : postDataString,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function (data, status, headers, config) {
var sessId = data.sessid;
var sessName = data.session_name;
$cookies[sessName] = sessId;
var xsrfUrl = 'http://mywebsite.com/services/session/token';
$http({
method: 'GET',
url: xsrfUrl
}).success(function (data, status, headers, config) {
$cookies["XSRF-TOKEN"] = data;
SessionService.setUserAuthenticated(true);
}).error(function (data, status, headers, config) {
console.log('error loading xsrf/csrf');
});
}).error(function (data, status, headers, config) {
if(data) {
console.log(data);
var msgText = data.join("\n");
alert(msgText);
} else {
alert('Unable to login');
}
});
};
The solution has to do with how the cookies need to be set and then passed through subsequent requests. Attempts to set them manually did not go well but the solution was simpler than I expected. Each $http call needs to set the options:
withCredentials: true
Another change I made was to use the term CSRF instead of XSRF, to be consistent with Drupal. I didn't use any built-in AngularJS CSRF functionality.
addItem: function(data)
{
return $http.post('api/programs/'+$stateParams.id+'/workouts', {item:data},{
headers:
{
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-CSRF-Token': $('meta[name="xxtkn"]').attr('content')
}
});
}
since it has been a year of this topic! not sure still encountering the same problem but for the ones who comes to search for answers here is how i handle it!
Pay attention the headers{} part i define a new header and call it X-CSRF-Token and grab value from the DOM of (serverside) generated html or php. It is not a good practise to also request the csrf token from the server.Cuz attacker could somehow request that as well. Since you save it as a cookie. Attacker can steal the cookie! No need to save it in a cookie! send the token with header and read it in the serverside to match it!
and for multitab of a same page issue. I use the same token thruout the whole session.
Only regenerate on login, logout and change of major site or user settings.
There is a great library callse ng-drupal-7-services. If you use this in you project it solves authentication / reauthentication and file / node creation aut of the box and you can fokuse on the importent stuff in your project.
So Authentication is there solved like this:
function login(loginData) {
//UserResource ahndles all requeste of the services 3.x user resource.
return UserResource
.login(loginData)
.success(function (responseData, status, headers, config) {
setAuthenticationHeaders(responseData.token);
setLastConnectTime(Date.now());
setConnectionState((responseData.user.uid === 0)?false:true)
setCookies(responseData.sessid, responseData.session_name);
setCurrentUser(responseData.user);
AuthenticationChannel.pubLoginConfirmed(responseData);
})
.error(function (responseError, status, headers, config) {
AuthenticationChannel.pubLoginFailed(responseError);
});
};
(function() {
'use strict';
AuthenticationHttpInterceptor.$inject = [ '$injector'];
function AuthenticationHttpInterceptor($injector) {
var intercepter = {
request : doRequestCongiguration,
};
return intercepter;
function doRequestCongiguration (config) {
var tokenHeaders = null;
// Need to manually retrieve dependencies with $injector.invoke
// because Authentication depends on $http, which doesn't exist during the
// configuration phase (when we are setting up interceptors).
// Using $injector.invoke ensures that we are provided with the
// dependencies after they have been created.
$injector.invoke(['AuthenticationService', function (AuthenticationService) {
tokenHeaders = AuthenticationService.getAuthenticationHeaders();
}]);
//add headers_______________________
//add Authorisation and X-CSRF-TOKEN if given
if (tokenHeaders) {
angular.extend(config.headers, tokenHeaders);
}
//add flags_________________________________________________
//add withCredentials to every request
//needed because we send cookies in our request headers
config.withCredentials = true;
return config;
};
There is also some kind of kitchen sink for this project here: Drupal-API-Explorer
Yes, each platform has their own convention in naming their tokens.
Here is a small lib put together hoping to make it easy to use with different platforms. This will allow you to use set names and could be used across all requests. It also works for cross-domain requests.
https://github.com/pasupulaphani/angular-csrf-cross-domain