How to make a request to a website, which has user authentication, to access its contents? - ios

I am working on an IOS app as my college project. Basically my college has a website which has login form[no sign up forms there], and students using their unique college ID and their own password can login into this website and access things like courses they have registered for, their grades etc. Now in my app i have a log in form and i want the students to be able to login with the same existing ID and password and access the same information as they would be able to do on the website itself. Simply speaking, i want to access the post authentication information on the website in my app. How do i approach this problem?

It depends if your school server allows request from different origins (different domain). If, yes just check when you login what info it posts and to which address. Then send a request to this address with the same info and check the result from the request.
Example:
Just use chrome or firefox dev inspector, fill the login form, send it and check the request done.
Let say the request done is a POST # http://myschool.com/login with username and password form, and the result if successful is a 200 code and a cookie. Then in node js you could do:
const querystring = require('querystring');
const request = require('request');
const form = {
username: 'usr',
password: 'pwd',
};
const formData = querystring.stringify(form);
const contentLength = formData.length;
request(
{
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: 'http://myschool.com/login',
body: formData,
method: 'POST'
},
function (err, res, body) {
// check the result here
}
);

Related

Amazon sp-api Authorisation Workflow Issue

Following the SP-API developer guide, I created an app to be able to access SP-API. The app is in published state now and we tried the authorisation workflow as per the amazon guide steps:
https://github.com/amzn/selling-partner-api-docs/blob/main/guides/en-US/developer-guide/SellingPartnerApiDeveloperGuide.md#amazon-seller-central-partner-network-authorization-workflow
We are using the website authorisation workflow, the steps 1-3 work as per the guide,
Step 1. Initiate the authorization from the Amazon Seller Central Partner Network.
Step 2. The selling partner consents to authorize your application, we authorise the app from seller Central and start the process, this url loads
https://sellercentral.amazon.com/apps/authorize/consent?application_id=amzn1.sp.solution.6cf699bd-f89a-4afd-b64e-7d21351aaaaa
Step 3. The selling partner signs into your website, we are redirected to the website login and that is done, then we get the amazon state and partner id as part of the url:
https://dashboard.aaa.com/api/amzn/login?amazon_callback_uri=https%3A%2F%2Fsellercentral.amazon.com%2Fapps%2Fauthorize%2Fconfirm%2Famzn1.sp.solution.6cf699bd-f89a-4afd-b64e-7d21351aaaa&amazon_state=MTY0MzgzMjI2NzU2OADvv71PY8KzGA0Iaih1Nzjvv71bHNqRGO-_ve-_ve-_ve-_vRnvv70a77-9eTF577-9Ee-_vRDvv70W77-9FEnvv73vv71ueO-_vR7vv73vv70PYu-_vUJd77-9B--_vTxF77-977-9f--_vQ%3D%3D&selling_partner_id=A2079RJZNKAAAA
Step 4. Amazon sends you the authorization information - Then we are redirected back to Seller Central -
https://sellercentral.amazon.com/apps/authorize/confirm/amzn1.sp.solution.6cf699bd-f89a-4afd-b64e-7d21351aaaa?state=1d477f90edfa493a1d15&amazon_state=MTY0MzgzMjI2NzU2OADvv71PY8KzGA0Iaih1Nzjvv71bHNqRGO-_ve-_ve-_ve-_vRnvv70a77-9eTF577-9Ee-_vRDvv70W77-9FEnvv73vv71ueO-_vR7vv73vv70PYu-_vUJd77-9B--_vTxF77-977-9f--_vQ==&selling_partner_id=A2079RJZNKAAAA&redirect_uri=https://dashboard.aaa.com/api/amzn/redirect
We are getting the sp_oauth_code in the url and tried using that to request for refresh token via POSTMAN request but unable to proceed to request the token from amazon oauth service.
We are getting error 400 as response, this is request format from the postman code section:
var axios = require('axios');
var qs = require('qs');
var data = qs.stringify({
'grant_type': 'authorization_code',
'code': 'ANnGBdMPnXAlrMyfUdwaaa',
'client_id': 'amzn1.application-oa2-client.cf71c857f2fd4f2c968851619bdaaaaa',
'client_secret': 'bba375434456e9917fcd5539c4324cc0e3182cccb9f1694ce3c63bee4f1aaaaa',
'redirect_uri': 'https://dashboard.aaa.com/api/amzn/redirect'
});
var config = {
method: 'post',
url: 'https://api.amazon.com/auth/o2/token',
headers: { },
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
We are sending data as x-www-form-urlencoded as Json in the POST request.
Any pointers on what we could differently or check for any missing params - any inputs on what to look for would be greatly appreciated.
The SP-API documentation is a little vague on this as it says you should include the grant type, code, seller id and secret as query parameters which by definition means within the url.
You actually need to add them to the body of the POST request.
This is an interesting discussion which revealed the above to me: https://github.com/amzn/selling-partner-api-docs/issues/79
Hope it helps.

Example of Login with Tiktok (web) flow

I would like a user to login with tikok on the web and get his basic information like:
avarat_url
union_id (uniq user identifier provided by tiktok)
display_name
The Tiktok Login Kit for Web Documentation seems to be missing a full example on how to implement the full sequence of calls. Also some things are not explained at all (like the callback URL). Can someone share their full solution with code example on how to integrate tiktok login onto a webpage.
Heres a full example of the tiktok login for web implementation:
setup a tiktok developer account https://developers.tiktok.com/
create a new app, this will generate a CLIENT_KEY and CLIENT_SECRET.
create 2 backend endpoints that you control, for example:
https://example.com/auth : builds a tiktok URL and redirects the user to that endpoint (where the user will be prompted to login).
https://example.com/authCallback : once the user has finished the login with tiktok flow, tiktok sends an authorizationResponse to this endpoint. The authorizationResponse contains info that you need to fetch the users data.
in section "Platform Info": insert the callback URL and redirect domain. The callback URL being the second of the 2 server endpoints listed above. Tiktok will send the authorizationResponse to that URL once the user successfully loggs in and grants or declines the required permissions. In redirect domain simply add the domain without the exact path.
fill out all info for your app and wait for approval, this can take up to 1-3 days.
once approved, you are ready to implement the full flow, which consists of multiple steps/requests.
(A) send the user from your frontend to your first backend endpoint https://example.com/auth. From there, the user will be redirected to the tiktok auth page.
(B) once the user finished the authorization, tiktok sends a authorizationResponse to your callback URL (https://example.com/authCallback), which contains a variable code. With the code you can request the access_token and open_id of the user.
(C) use the access_token and open_id to request basic user info.
(A) Send User to Tiktok Authentication Page
In your frontend, redirect the user to https://example.com/auth. Then run the following nodejs backend code on your /auth route. For this example we use an express app (req = request object, res = response object):
// IMPORTANT, it is your responsibility to store a csrf token
// in your database, to be able to prevent xss attacks, read more
// here (section 2.1) => https://developers.tiktok.com/doc/login-kit-web
const createCsrfState = () => Math.random().toString(36).substring(7);
const csrfState = createCsrfState();
res.cookie('csrfState', csrfState, { maxAge: 60000 });
let url = 'https://open-api.tiktok.com/platform/oauth/connect/';
url += `?client_key=${CLIENT_KEY}`;
url += '&scope=user.info.basic';
url += '&response_type=code';
url += `&redirect_uri=${encodeURIComponent('https://example.com/authCallback')}`;
url += '&state=' + csrfState;
// redirect the user to the generated URL
// user will be prompted to login with tiktok
// and authorize needed permissions
res.redirect(url);
This code redirects the user to a tiktok url, where the user is prompted to sign in with tiktok and grant access.
(B) Handle authorizationResponse, use code to get access_token and open_id
Once the user finished the login process, tiktok sends an authorizationResponse to your second backend server endpoint https://example.com/authCallback. In that callback you recieve variables state and code.
// express example with
// `req` = request object
// `res` = response object
// check if the csrf token is valid
// its the developers responsibility
// to setup a validation logic.
if (!validateCsrfToken(req.query.state)) {
throw new Error("invalid csrf token");
}
async function getAccessTokenAndOpenId(code, TIKTOK_CLIENT_KEY, TIKTOK_CLIENT_SECRET) {
let urlAccessToken = 'https://open-api.tiktok.com/oauth/access_token/';
urlAccessToken += '?client_key=' + TIKTOK_CLIENT_KEY;
urlAccessToken += '&client_secret=' + TIKTOK_CLIENT_SECRET;
urlAccessToken += '&code=' + code;
urlAccessToken += '&grant_type=authorization_code';
const resp = await axios.post(urlAccessToken);
return {
accessToken: resp.data.data.access_token,
openId: resp.data.data.open_id,
};
}
const code = req.query.code;
const { openId, accessToken } = await getAccessTokenAndOpenId(code, TIKTOK_CLIENT_KEY, TIKTOK_CLIENT_SECRET);
(C) Get basic user info
async function getBasicInfo(accessToken, openId) {
let urlBasicInfo = `https://open-api.tiktok.com/user/info/`;
const data = {
access_token: accessToken,
open_id: openId,
fields: [
"open_id",
"union_id",
"avatar_url",
"avatar_url_100",
"avatar_url_200",
"avatar_large_url",
"display_name",
],
};
const resp = await axios.post(urlBasicInfo, data);
return resp.data.data.user;
}
const userBasicInfo = await getBasicInfo(accessToken, openId);
// šŸ„³ done!

How to create SendBird user with SendBird Platform API and Request node library

We are building a react-native chat app. We are implementing a back end authentication solution on google Firebase. The creation of a new user in Firebase Auth triggers a cloud function which should create a new SendBird user with an access token. The access token will be stored in Cloud Firestore, ready for retrieval the next time the user logs in.
We are having trouble implementing the POST request that creates the new user via the platform API. We are using the Request library for node.js. We are able to reach the API endpoint, but the following object is returned: { message: 'SendBird API Endpoint.', error: true }. There is no indication of what the error may be.
This happens when sending the request to the base url. When we send the request to /users or /v3/users, we receive a 403 error.
Any indication as to what may be causing this problem would be greatly appreciated.
Below is the cloud function index.js code
const functions = require('firebase-functions');
const admin = require("firebase-admin");
const request = require('request');
admin.initializeApp();
exports.handleNewUser = functions.auth.user().onCreate((user) => {
var newUserRequestBody = {
"user_id": user.email,
"nickname": user.email,
"profile_url": "",
"issue_access_token": true,
}
request.post({
headers: {
'Content-Type': 'application/json, charset=utf8',
'Api-Token': // API Token
},
url: 'https://api-{application_id}.sendbird.com',
form: newUserRequestBody
}, function(error, response, body){
if (!error && response.statusCode === 200) {
const info = JSON.parse(body);
console.log("request successful");
console.log(response.statusCode);
console.log(info);
}
else{
console.log("request unsuccessful");
console.log(response.statusCode);
console.log(error);
}
});
return null;
});
Did you try with full path of end point to url: (including /v3/users)?
Or you may need to use "baseUrl" like below?
https://github.com/request/request#requestoptions-callback
Also, you need to make sure that you correctly used {application_id} value and {API Token} value.
You can double check this from your dashboard of SendBird.
http://dashboard.sendbird.com > Log in with your ID > select your APP.
There is a section named "App credentials" in "Overview" menu.
You can double check your API-request URL and API-Token value from there.

How can I store and set the user on this angularjs devise library auth service? Or is it being done already?

I am using the cloudspace angularjs-devise library on the client. When I try to login/register I get a 200 ok response with the plain user object visible in the chrome js console. Refreshing the page seems to lose this information even though I assumed that the service would store this at some point since it also has logout and currentUser methods.
https://github.com/cloudspace/angular_devise
My questions are:
1) Is this service actually storing the user and if so how (i.e. with cookies or localstorage or in memory)?
2) If the service does not store the user how can I store this information in a custom cookie/localstorage and more importantly set the user into the service so that the services "isauthenticated" and "currentuser" methods can be used?
Partial Library Readme Instructions
Just register Devise as a dependency for your module. Then, the Auth service will be available for use.
angular.module('myModule', ['Devise']).
config(function(AuthProvider) {
// Configure Auth service with AuthProvider
}).
controller('myCtrl', function(Auth) {
// Use your configured Auth service.
});
Auth.login(creds): Use Auth.login() to authenticate with the server. Keep in mind, credentials are sent in plaintext; use a SSL connection to secure them. creds is an object which should contain any credentials needed to authenticate with the server. Auth.login() will return a promise that will resolve to the logged-in user. See AuthProvider.parse() for parsing the user into a usable object.
angular.module('myModule', ['Devise']).
controller('myCtrl', function(Auth) {
var credentials = {
email: 'user#domain.com',
password: 'password1'
};
Auth.login(credentials).then(function(user) {
console.log(user); // => {id: 1, ect: '...'}
}, function(error) {
// Authentication failed...
});
});
My partial code:
main.js
var myApp = angular.module('mail_app', ['ngRoute', 'ngResource', 'Devise']);
myApp.config(function($routeProvider, $locationProvider, $httpProvider, AuthProvider) {
console.log("in router")
$locationProvider.html5Mode(true);
$httpProvider.defaults.headers.common['X-CSRF-Token'] =
$('meta[name=csrf-token]').attr('content');
$httpProvider.defaults.headers.common['ClientType'] = 'browser';
// Customise login
AuthProvider.loginMethod('POST');
AuthProvider.loginPath('/api/v1/users/login.json');
// Customise register
AuthProvider.registerMethod('POST');
AuthProvider.registerPath('/api/v1/users.json');
});
SessionsController.js
myApp.controller('SessionsController', ['$scope', 'Auth', '$http', function($scope, Auth, $http) {
console.log("in session controller")
console.log(Auth.isAuthenticated());
$scope.loginUser = function() {
console.log("in login")
var credentials = {
email: $scope.email,
password: $scope.password
};
Auth.login(credentials).then(function(user) {
$scope.authError = 'Success!';
console.log(user); // => {id: 1, ect: '...'}
Auth.currentUser = user;
}, function(error) {
$scope.authError = 'Authentication failed...';
});
};
$scope.registerUser = function(){
console.log("in register function")
var ncredentials = {
email: $scope.newEmail,
password: $scope.newPassword,
password_confirmation: $scope.newPasswordConfirmation
};
Auth.register(ncredentials).then(function(registeredUser) {
console.log(registeredUser); // => {id: 1, ect: '...'};
}, function(error) {
$scope.authError = 'Registration failed...';
});
};
$scope.getCurrentUser = function(){
Auth.currentUser().then(function(user) {
// User was logged in, or Devise returned
// previously authenticated session.
console.log(user); // => {id: 1, ect: '...'}
$scope.id = user.id;
}, function(error) {
// unauthenticated error
});
};
$scope.isUserAuthenticated = function(){
Auth.isAuthenticated();
};
}]);
First of all you need to understand how cookies and sessions work in Rails.
From this article:
Rails uses a CookieStore to handle sessions. What it means is that all
the informations needed to identify a user's session is sent to the
client and nothing is stored on the server. When a user sends a
request, the session's cookie is processed and validated so rails,
warden, devise, etc. can figure out who you are and instantiate the
correct user from the database.
What this means is that on every request, Rails will look up at the session cookie, decode it and get something like
cookie = {
"session_id": "Value",
"_csrf_token": "token",
"user_id": "1"
}
At that point Rails knows that the current user has id=1 and can make a sql query. (Like current_user = User.find(1)).
When a user is logged in, a cookie is created, when the user is logged out - the cookie is destroyed. If Rails doesn't find a cookie or the cookie doesn't have information about the current user, devise will assume that the user is not logged in (current_user is nil)
Even if you login through ajax (to be particular it is through the 'angular_devise' gem in your case) the cookie is created. It is not stored on the server, but in the browser. (This is why if you are logged in one browser, you are not automatically logged in another browser) As you pointed out the library doesn't keep information who is logged in, and this is because the information is stored in a cookie and the library cannot decode the cookie without help from the server.
This is why you will have to make a call to get the current user if the user refreshes the page. (Sorry)
The way to get the current_user is very simple. This is the cleanest solution I found.
# application_controller.rb
def me
render json: current_user
end
# routes.rb
get "me" => "application#me"
// main.js
// I am not familiar with angular_devise lib but you get the point:
// this method fetches from server when myApp is initialized (e.g. on page reload)
// and assigns the current_user so he/she can be used by the app
myApp.run(["AuthService", function(AuthService) {
AuthService.getUserFromServer();
}]);
If you have to load data specific to the user, you will have to load the user first and then the data. Needless to say you will have to use promises.
TL;DR: You will have to ask the server
I am open for questions and comments.
I guess your problem is the refresh. The angular-devise lib is probably assuming you are in a SPA (Singe Page Application) so it should not refresh. With this assumption, angular-devise can store all the information in memory. When you refresh your page, you basically bootstrap the application from zero. And the request to server is probably issued by your code when application is starting. You probably call Auth.currentUser() somewhere on start of the application
Had same problem. Just use that gem
https://github.com/jsanders/angular_rails_csrf
You can also get rid of "protect_from_forgery" in your application controller, but this is very risky.

Authentication for Node.js App with Angular.js and iOS Clients

I've tried to read as many different answers and posts as possible, but I still can't quite settle on a solution that fits my needs. I'm trying to work out the best (most efficient, but mostly more secure) way to handle user authentication, log in, etc.
I have a Node.js server, running on Express; I have an Angular.js web app; and I have an iOS app. I expose a RESTful API with Express/Node.js.
Cookies
The first things I read said to use cookies, and to store a session id/login token on the server side (hashed) and on the client side (unhashed). The client would transfer this id with each request, the server would hash it, parse it and process the request accordingly. This does not feel RESTful (not a huge issue), but more importantly, would I have to duplicate my API: one for username/password authentication (e.g. done via curl) and one for cookie-based authentication (e.g. my web app)?
Another problem with this: what I would do if I had multiple connections from the one user, e.g. they're logged in in two browsers, an iPhone and an iPad. Would my storage of their session ids need to now be an array?
HTTP Basic Auth
The next idea was to use HTTP Basic Auth (with SSL), which seems easy enough, but is not recommended because you need to transfer a username and password with each request. If I were to do it with HTTP Basic Auth, would I then store the username and password in cookies (or HTML local storage) to allow for 'Remember Me' functionality? Or could I combine the two: use HTTP Basic Auth for the actual requests (post a new post, etc.) and just use a session id stored in a cookie for the initial log in sequence/remember me aspects?
Is transmitting a session id more secure than just transmitting the user's password? How?
The session id is going to act ostensibly as a password, so to me transmitting it would have the same security issues as transmitting a password.
Basic Auth seems to be supported across all platforms, which is ideal. The main downside seems to be needing to transfer client authentication data with each request. Is there a way to mitigate this issue?
OAuth
OAuth seems like overkill for my needs. I think I would lose the ability to do curl commands to test my API. How is OAuth an improvement over the cookies method?
As you can probably tell, I'm a little confused by the diverse information available, so if you have a set of good linksā€”applicable to this scenarioā€”I would love to read them. I'm trying to find a solution that fits across all platforms, but is still as secure as possible. Also, if I have any of my terminology wrong, please correct me because it will make searching easier for me.
Thanks.
Update:
I've been thinking about this problem, and I've had an idea. Please tell me if this is dumb/insecure/any feedback, because I'm not sure if it's good.
When the user logs in, we generate a random session id (salted etc.). This optional session id is sent to the client, which the client can store (e.g. in cookies) if they choose; the session id is stored in the database.
This session id is then optionally sent with each request as either an HTTP Authentication header or query string, or the client can just send the username and password if they want (which gives us our regular REST API). At the server end, we check first for a session id parameter, if it's not present, we check for username/password. If neither are thereā€”error.
On the server, we check that the session id is associated with the correct username. If it is, we complete the request.
Every time the user logs in, we create a new session id or delete the current one, and send this with the response to the log in request.
I think this lets me use the regular REST API, where appropriate, with Basic Auth, and maintain sessions/remember me functionality. It doesn't solve the multiple log ins issue, but otherwise I think this way should would. Please let me know.
I would use a token based authentication where you can send a token (automatically) with each request. You'll have to log in once, the server will provide you with a token which you can then use to send with each request. This token will be added to the HTML header, so that you don't have to modify each request to the browser.
You can set certain calls in the API so that they always need a token, while others might not be token protected.
For Express, you can use express-jwt (https://www.npmjs.org/package/express-jwt)
var expressJwt = require('express-jwt');
// Protect the /api routes with JWT
app.use('/api', expressJwt({secret: secret}));
app.use(express.json());
app.use(express.urlencoded());
If you want to authenticate you can create this function in your express server:
app.post('/authenticate', function (req, res) {
//if is invalid, return 401
if (!(req.body.username === 'john.doe' && req.body.password === 'foobar')) {
res.send(401, 'Wrong user or password');
return;
}
var profile = {
first_name: 'John',
last_name: 'Doe',
email: 'john#doe.com',
id: 123
};
// We are sending the profile inside the token
var token = jwt.sign(profile, secret, { expiresInMinutes: 60*5 });
res.json({ token: token });
});
And for protected calls something that starts with /api:
app.get('/api/restricted', function (req, res) {
console.log('user ' + req.user.email + ' is calling /api/restricted');
res.json({
name: 'foo'
});
});
In your Angular application you can login with:
$http
.post('/authenticate', $scope.user)
.success(function (data, status, headers, config) {
$window.sessionStorage.token = data.token;
$scope.message = 'Welcome';
})
.error(function (data, status, headers, config) {
// Erase the token if the user fails to log in
delete $window.sessionStorage.token;
// Handle login errors here
$scope.message = 'Error: Invalid user or password';
});
And by creating an authentication interceptor, it will automatically send the token with every request:
myApp.factory('authInterceptor', function ($rootScope, $q, $window) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
}
return config;
},
response: function (response) {
if (response.status === 401) {
// handle the case where the user is not authenticated
}
return response || $q.when(response);
}
};
});
myApp.config(function ($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
});
If you have to support old browsers which do not support local storage. You can swap the $window.sessionStorage with a library like AmplifyJS (http://amplifyjs.com/). Amplify for example uses whatever localstorage is available. This would translate in something like this:
if (data.status === 'OK') {
//Save the data using Amplify.js
localStorage.save('sessionToken', data.token);
//This doesn't work on the file protocol or on some older browsers
//$window.sessionStorage.token = data.token;
$location.path('/pep');
}
}).error(function (error) {
// Erase the token if the user fails to log in
localStorage.save('sessionToken', null);
// Handle login errors here
$scope.message = 'Error: Invalid user or password';
});
And the authintercepter we swap for:
angular.module('myApp.authInterceptor', ['myApp.localStorage']).factory('authInterceptor', [
'$rootScope',
'$q',
'localStorage',
function ($rootScope, $q, localStorage) {
return {
request: function (config) {
config.headers = config.headers || {};
config.headers.Authorization = 'Bearer ' + localStorage.retrieve('sessionToken');
return config;
},
response: function (response) {
if (response.status === 401) {
}
return response || $q.when(response);
}
};
}
]);
You can find everything except AmplifyJS in this article:
http://blog.auth0.com/2014/01/07/angularjs-authentication-with-cookies-vs-token/
Have a look to the yeoman generator for angular and node? The generator-angular-fullstack have a very nice structure for user authentification using passport.
You can see an example here :
the code: https://github.com/DaftMonk/fullstack-demo
the result: http://fullstack-demo.herokuapp.com/
Hope it helps!
I use generator-angular-fullstack, the /api services are not secured, get your _id from /api/users/me, logout, and go to /api/users/your_id_here, you will figure out that the /api not secured.

Resources