Jhipster + REST client + authentication - rest

I need to understand how to authenticate a REST client (could be Paw, could be an android app, an iOs app using AFNetworking with jHipster and I think, more in general, with spring-boot of which I am no expert).
While I am able to obtain a token when logged in a browser, and subsequently use this token in the following requests, I do not understand how I can authenticate in the first place using RESTful best practices.
For example, in Paw.app, I can pass a Basic authentication, or Oauth2, but I don't understand how to get the session token simply authenticating as I do on a web browser.
Similarly, in AFNetworking I am able to pass basic authentication, e.g.
NSString*auth=[NSString stringWithFormat:#"%#:%#", #"admin", #"admin"];
NSString *authValue = [NSString stringWithFormat:#"Basic %#", [auth base64EncodedString]];
[manager.requestSerializer setValue:authValue forHTTPHeaderField:#"Authorization"];
But I struggle to understand how to authenticate with the session security which is bundled in jHipster/spring boot.

First of all, do not use HTTP session authentication for mobile apps.
On the other hand, Oauth2 or JWT works fine with mobile apps. The basic idea behind them is to get a token from Jhipster to mobile the token has an expiry time. In that time you can use the token to access any REST API of Jhipster to access data.
Below I am showing the code snippet of how I was using the Jhipster rest API in my angularjs based ionic app. I hope it gives the idea of what you need to do.
uncomment cors in application.yml inside jhipster
cors: #By default CORS are not enabled. Uncomment to enable.
allowed-origins: "*"
allowed-methods: GET, PUT, POST, DELETE, OPTIONS
allowed-headers: "*"
exposed-headers:
allow-credentials: true
max-age: 1800
To access REST API with Oauth2 authentication in ionic you must first get the token in the ionic app by
$http({
method: "post",
url: "http://192.168.0.4:8085/[Your app name]/oauth/token",
data: "username=admin&password=admin&grant_type=password&scope=read write&client_secret=my-secret-token-to-change-in-production&client_id=auth2Sconnectapp",
withCredentials: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
'Authorization': 'Basic ' + 'YXV0aDJTY29ubmVjdGFwcDpteS1zZWNyZXQtdG9rZW4tdG8tY2hhbmdlLWluLXByb2R1Y3Rpb24='
}
})
.success(function(data) {
alert("success: " + data);
})
.error(function(data, status) {
alert("ERROR: " + data);
});
Here "YXV0aDJTY29ubmVjdGFwcDpteS1zZWNyZXQtdG9rZW4tdG8tY2hhbmdlLWluLXByb2R1Y3Rpb24=" is equal to (clientId + ":" + clientSecret)--all base64-encoded
The above $http if successful will give you this JSON which contains token and it's expiry time
{
"access_token": "2ce14f67-e91b-411e-89fa-8169e11a1c04",
"token_type": "bearer",
"refresh_token": "37baee3c-f4fe-4340-8997-8d7849821d00",
"expires_in": 525,
"scope": "read write"
}
Take notice of "access_token" and "token_type" if you want to access any API this is what you have to use. We send the token with API to access data until the token expires then we either refresh it or access for a new one.
For example
$http({
method: "get",
url: "http://192.168.0.4:8085/auth-2-sconnect/api/countries",
withCredentials: true,
headers: {
'Authorization':' [token_type] + [space] + [access_token] '
}
})
.success(function(data) {
alert("success: " + data);
})
.error(function(data, status) {
alert("ERROR: " + data);
});

Here a summarisation of how I implemented the solution. It’s real swift code, but please take it as pseudocode, as it might be incorrect.
make a call to whatever method you need to call, passing in such method a callback (block, or equivalent) for the success and one for the failure
func action(
URLString:String,
method:Method,
encoding:Encoding = .JSON,
parameters:[String : AnyObject]?,
success:(statusCode:Int, responseObject:AnyObject)->Void,
failure:(statusCode:Int, error:NSError)->Void
)
Inside the method es. /events you handle a particular case of failure, which is when the status code is 401.
if(r!.statusCode==ResponseCodes.HTTP_UNAUTHORIZED.rawValue){
loginAndAction(URLString, method: method, encoding: encoding, parameters: parameters, success: success, failure: failure)
}else{
failure(statusCode: response.response!.statusCode, error:response.result.error!)
}
In this particular case, instead of returning back the result and calling the failure callback, you call a login() method which, after the necessary parameters, accept the original success() callback
func loginAndAction(
URLString:String,
method:Method,
encoding: Encoding,
parameters:[String:AnyObject]?,
success:(statusCode:Int, responseObject:AnyObject)->Void,
failure:(statusCode:Int, error:NSError)->Void
)->Void
if the authentication succeeds
var d:[String:AnyObject] = response.result.value as! [String:AnyObject]
self.authToken = d["access_token"] as! String
action(URLString, method: method,encoding:encoding, parameters: parameters, success: success, failure: failure)
at this point the method action could use a proper working token.
This should happen only once a day (based on the token expiration), and it is a mechanism appliable to the oauth2 refresh_token call.

Related

jwt acess_token and refresh_token mechanism: axios : How to keep checking for the access_token is working

I am using JWT token based authentication system. i.e djangorestframework-simplejwt in my backend
Now I am using reactj and axios as frontend:
After providing username and pass to the login api, I got access_token and refresh_token which I stored in the localstorage
Now I am trying to connect to an api using access_token.
I get Token invalid or expired
Example I am trying to change password using this api and provide access_token
const url = "dj-rest-auth/password/change/";
const auth = {
headers: {
Authorization: "Bearer " + localStorage.getItem("access_token"),
Accept: "application/json",
"Content-Type": "application/json",
},
};
const data = {
old_password: old_password,
new_password1: new_password1,
new_password2: new_password2,
};
const promise = axios.post(url, data, auth);
promise
.then((res) => {
console.log(res)
})
.catch((err) => {
if (err.response) {
console.log(`${err.response.status} :: ${err.response.statusText}`)
console.log(err.response.data)
}
})
I can do another api call using refresh_token to get access_token when i get an err.
But sometimes, the err can be due to network error or something else. Then even i try to get access_token using refresh_token, it will just get into a loop.
HOw to do this the right way
If you are using Django as the backend, I would suggest using dj-rest-auth for JWT token authentication. dj-rest-auth requires "djangorestframework-simplejwt" for token management.
It is recommended to store access token and refresh token in httponly cookie so that it is not accessed by javascript.
Add JWTtokenAuthentication as authentication classes in settings.py.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'dj_rest_auth.jwt_auth.JWTCookieAuthentication'
]
}
Add the below configuration too in settings.py
REST_SESSION_LOGIN = False
SITE_ID=1
REST_USE_JWT = True
JWT_AUTH_COOKIE = 'access-token' #any name
JWT_AUTH_REFRESH_COOKIE = 'refresh_token' #any name
JWT_AUTH_SECURE = True
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
There is an open issue with dj-rest-auth, that requires the below code to be implemented in your back-end Github issue: https://github.com/iMerica/dj-rest-auth/issues/97. As workaround suggested, you have to create a file middleware.py and paste below code.
import json
from django.utils.deprecation import MiddlewareMixin
from yourapp.settings import JWT_AUTH_REFRESH_COOKIE # from settings.py
class MoveJWTRefreshCookieIntoTheBody(MiddlewareMixin):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_view(self, request, view_func, *view_args, **view_kwargs):
if request.path == '/token/refresh/' and JWT_AUTH_REFRESH_COOKIE in request.COOKIES:
if request.body != b'':
data = json.loads(request.body)
data['refresh'] = request.COOKIES[JWT_AUTH_REFRESH_COOKIE]
request._body = json.dumps(data).encode('utf-8')
else:
print("The incoming request body must be set to an empty object.")
return None
By now, your back-end will be successfully generating access token and refresh token. Even your back-end will be capable of refreshing access token using refresh token.
Front-End:
By default, access token and refresh tokens are stored in httponly cookie, so you don't need to worry about that part.
Axios can be used to make call to login-end point to get tokens. Make sure you use "withCredentials" and "Headers" in your request.
Response will be tokens, by default it will be stored in httponly cookie, since we are using dj-rest-auth. For all the consecutive requests, httponly cookie will be included, if tokens are valid, user will be provided access. IF token is expired, you need to make call to refresh endpoint to get new access token.
Since you are in development mode, you have to have same domain for both BE and FE, different ports.You can start django-server using below command and make sure your FE is also running in localhost
python manage.py runserver localhost:8080
dj-rest-auth : https://dj-rest-auth.readthedocs.io/en/latest/index.html

Flask JWT Extended- Different locations for access_token (header) & refresh_token(httpOnly cookie)

How to configure flask app with flask-jwt-extended for which we need something like below.
AccessToken/Bearer must sent as a Header (and not cookie)
RefreshToken must sent as httpOnlyCookie for /api/refreshtoken path only
How to set two different token one in header and one in cookie? We are able to set either both as cookie or both as a header.
Any help?
Thanks
Raxit
I wanted to do the same while building a React + Flask single page application after days of headache trying to understand authorization and authentication as I am a beginner.
Anyways, I managed to do it this way:
In Flask, config:
app.config['JWT_TOKEN_LOCATION'] = ['headers', 'cookies']
app.config['JWT_REFRESH_COOKIE_PATH'] = '/auth/refresh'
And what I return in my login function:
resp = jsonify({'access_token': access_token})
set_refresh_cookies(resp, refresh_token)
return resp, 200
And in my refresh function:
# Refresh access token
#app.route('/auth/refresh', methods=['POST'])
#jwt_refresh_token_required
def refresh():
user = get_jwt_identity()
resp = {
'access_token': create_access_token(
identity={
'username': user['username'],
'role': user['role']
},
expires_delta=timedelta(seconds=600),
user_claims=user['role']
)
}
return jsonify(resp), 200
And on the front side, I collect the JSON access_token and set it in memory and use withCredentials to send the refresh_token with my API calls.
axios.defaults.withCredentials = true;
axios.defaults.headers.common['Authorization'] = `Bearer ${access_token}`;
more precisely:
.then(({ data: { access_token } }) => {
axiosHttp.defaults.headers.common['Authorization'] = `Bearer ${access_token}`;
return jwt_decode(access_token);
})
then I use the data from my decoded access_token in a React Context Component to authenticate access to pages depending on roles.
logout is simply setting to null my context and calling the api to unset the refresh cookie
#app.route('/auth/logout', methods=['DELETE'])
#jwt_required
def logout():
resp = jsonify({"msg": "Successfully logged out"})
unset_jwt_cookies(resp)
return resp, 200
it's quite simple in the end but it took me quite a while to figure out!

Transferring bearer token from API request to other tests using Cypress

I am running tests on a web application to test WYSIWYG functionality and XSS vulnerabilities. I use the following command before
beforeEACH(() => {
cy.vaderAuth()
});
Cypress.Commands.add('vaderAuth', () => {
let config = Cypress.config();
cy.request({
method: 'POST',
url: Cypress.config('tokenUrl') + '/api/v1?actions=api/v1/login',
body: {
username: config.horizon.username,
password: config.horizon.password,
}
}).then(response => {
window.localStorage.setItem('JWT', response.body['api/v1/login'].token)
})
})
The above command successfully gets the Bearer Token but then the following test throws a 401 Unauthorised error.
it('Sending a regular API request to Welcome Messages', function() {
cy.request({
method : 'POST',
url : '/api/v1/organisations/welcome-message?actions=api/v1/organisations/welcome-message/set-welcome-messages',
body : {"welcome_messages":[{"message":"<p>Test investor welcome message!</p>","type":"User"},{"message":"<p>Internal User welcome message Cypress Test. </p>","type":"Internal User"},{"message":"<p>Test entrepreneur welcome message!</p>","type":"Entrepreneur"}]},
headers : {"Authorization": "Bearer " + window.localStorage.JWT}
})
})
beforeEach working, API request 401
401 cypress error
I have checked the failure and it is sending the Bearer Token as a header, and the request headers match the headers the same request sends if I enter the Bearer Token manually as follows.
it('Sending a regular API request to Welcome Messages', function() {
cy.request({
method : 'POST',
url : '/api/v1/organisations/welcome-message?actions=api/v1/organisations/welcome-message/set-welcome-messages',
body : {"welcome_messages":[{"message":"<p>Test investor welcome message!</p>","type":"User"},{"message":"<p>Internal User welcome message Cypress Test. </p>","type":"Internal User"},{"message":"<p>Test entrepreneur welcome message!</p>","type":"Entrepreneur"}]},
//headers : {"Authorization": "Bearer (insertbearertokenhere)}
})
})
When entering the Bearer Token manually the following tests all run smoothly with no error, but if I have to go in and change the Bearer Token every time I run these tests, it kind of defeats the point of automating it as by the time I've logged into my web app and got the token, I may as well test the application manually.
Any help on this would be greatly appreciated as I have been working on it with no luck for around a week now.

Using Axios as an Alternative to request in nodejs

I am building a flutter application that requires oauth 1 authorization for one of the third party services I am using. Because flutter oauth 1 package is restricted I decided to use the oauth 1 package that npm provides. This is the code that is used to access the user generated access token from the site.
I previously used request to make a call to the api endpoint first, to access the token and secondly to use the token recieved to make another call to a different resource endpoint
How can I use axios to make the same request, emphasis on the fact that each request needs a hmac-sha1 signed signature in the header.
Thank you.
consumer: {
key: CONSUMER KEY,
secret: CONSUMER SECRET,
},
signature_method: 'HMAC-SHA1',
hash_function(base_string, key) {
return crypto
.createHmac('sha1', key)
.update(base_string)
.digest('base64')
},
})
const request_data = {
url: 'https://www.instapaper.com/api/1/oauth/access_token/',
method: 'POST',
data: { x_auth_username : USERNAME , x_auth_password : PASSWORD , x_auth_mode : 'client_auth' },
}
request(
{
url: request_data.url,
form: request_data.data,
method: request_data.method,
headers: oauth.toHeader(oauth.authorize(request_data)),
},
function(error, response, body) {
// Process your data here
console.log(error);
console.log(response);
console.log(body);
}
)
Finally found the answer for this link to the issue created on github
https://github.com/axios/axios/issues/2771

Angular 6 - Add JWT bearer token to header not working

I'm trying to add the auth bearer token header while getting a comment from the asp.net core 2.2 backend in angular 6
getComment(postId: number): Observable<IComment[]>{
let headers = new HttpHeaders();
headers.append('Content-Type', 'application/json');
let authToken = localStorage.getItem('auth_token');
headers.append('Authorization', 'Bearer ' + authToken);
console.log(authToken);
return this.httpClient.get<IComment[]>('api/comment/post/' + postId, { headers });
}
This piece of code is not working. I am getting a value from console.log(authToken). When I copy the token in Postman, everything is working fine.
My login function in a service. This is working fine to, i'm getting the token from the backend.
login(login: ILogin) {
console.log(login);
return this.http
.post('api/auth/login', login)
.pipe(map((res: any) => {
localStorage.setItem('auth_token', res.auth_token);
this.loggedIn = true;
this._authNavStatusSource.next(true);
return true;
}));
}
When I remove authorization from the action in the backend, getting the comments is working fine. As you can see in the image below, the jwt token is just not being add to the header.
Postman:
Header information from chrome
You are not passing the headers in { headers } section.
Change return this.httpClient.get<IComment[]>('api/comment/post/' + postId, { headers }); to return this.httpClient.get<IComment[]>('api/comment/post/' + postId, { headers: headers });
When you say it's working fine via Postman, and that this is not a CORS issue (i.e., either CORS is enabled, or your JS is being served from the same origin as you API), I assume you're already subscribing to the returned Observable<IComment[]>.
The code above won't issue the request until there is a call somewhere that looks like this:
yourService.getComment(postId).subscribe(comments => { ... });
That will begin consuming the Observable and trigger the underlying HTTP request.