mobile app development - how to create a server implementation - php

EDIT Originally I thought Oauth2 is the way to go but maybe it is not. I'll leave that out of this question for now as it is confusing things.
I'm creating a mobile app (Android/iOS). I'd like the user to enter their credentials (user/pass) in the mobile device which would then get sent to my server (Joomla CMS) to verify the credentials and create/send a token. I don't want to store the user/pass on the device just the token.
In addition this token needs to have a timeout to be refreshed when needed. Such as credentials have changed.
At this point I'm trying to figure out what the architecture of this will look like.
Are there any tutorials on how you can achieve this (ideally with Joomla)? Anything that someone could point me to?

You should post the username and password from the mobile app and from there on you should follow the solution provided in this question:
https://stackoverflow.com/a/2188969/900617

The end solution is to create my own Joomla component. Pretty much everything is in my controller. Not the final code but something like this will work.
defined('_JEXEC') or die;
jimport('joomla.application.component.controller');
class FooauthController extends JController
{
function __construct() {
// params
$jinput = JFactory::getApplication()->input;
$this->username = $jinput->get('user', '', 'STRING');
$this->password = $jinput->get('password', '', 'STRING');
$this->checkParameters();
}
private function checkParameters() {
// datatype checks
if ($this->username == '' || $this->password == '') {
header('HTTP/1.1 400 Bad Request', true, 400);
}
}
private function createToken() {
// token generation - what Joomla does (just an example)
jimport('joomla.user.helper');
$salt = JUserHelper::genRandomPassword(32);
$crypted = JUserHelper::getCryptedPassword($password, $salt);
$cpassword = $crypted.':'.$salt;
return $cpassword;
}
function execute() {
// Get the global JAuthentication object
jimport( 'joomla.user.authentication');
$auth = & JAuthentication::getInstance();
$credentials = array( 'username' => $this->username, 'password' => $this->password );
$options = array();
$response = $auth->authenticate($credentials, $options);
// success
if ($response->status === JAUTHENTICATE_STATUS_SUCCESS) {
$response->status = true;
echo json_encode($this->createToken());
} else {
// failed
$response->status = false;
echo json_encode($response);
}
}
}
This represents a component called com_fooauth. Now the native app will send a query like this:
http://www.myhost.com/index.php?option=com_fooauth&user=username&password=pass&format=raw
Kind of a short cut to put everything in the controller, but hopefully you get the idea.

I hope that I understand correctly your use case.
If you want to use oAuth, then your mobile apps are considered as the oAuth-client.
Your "server" holds the "protected resources", and it can be used only with oAuth access-token, so it is called "resource server". Now you want something to supply this access-token, so this is the identity-provider, AKA authentication server, e.g. Facebook, Google, (or implement one by your own).
The flow is (generally): the user (mobile app) tries to reach a protected resource; since it has no token, he is being redirected to the auth-server. the latter is responsible for the user/password login page, and creating the token.
If it is true - you still can implement everything by your own, without using Facebook/Google APIs, because oAuth has SPECs. However, it can be easier for you to use the providers' packages.
EDIT: reconsider the usage of oAuth
You use oAuth only if you want your webapp to support oAuth SPEC. There are several benefits, one of them is that you can use 3rd party identity provider, e.g. Yahoo! and use their identities without managing them. So if I have a user in Yahoo!, I can use your app without additional registrations (your app will have to support access-tokens from Yahoo!). But in your case, you are about to implement all the logic of identity-provider (forgot password, change password, registration, etc) plus supporting oAuth - and all of this without enjoying the benefits of oAuth at all! So - you have to reconsider the usage of oAuth...

You need to use their APIs as a base. They aren't going to just let you build your own API that connects to their database, that to them would look more like a password cracker than an API.

This isn't Joomla or a tutorial, (and I'm very rusty in php) that said...
First a few caveats:
* memcache isn't secure & this implementation has you putting username / password in: Be sure that it is safely behind a firewall, or else encrypt it first. Happy to give some pointers on that if you need it.
* memcache isn't guaranteed not to drop data if it runs out of memory. In practice it is reliable, but your app should handle that gracefully. If you don't want to lose data like that, just substitute something like couchbase for memcache.
* just returning a token in response to a login probably isn't super useful. I'd json-ize the token along with stuff like the user name, and any other info to get the app up and running without needing to make a second API call.
* the code below doesn't handle error cases, I can call them all out in more detail if that isn't obvious to you.
If it were me, I'd just use memcache to persist the tokens & map that token to the username & password that was originally passed. You can use the memcache time to live to get your expiration for free.
Send username / password to the server (ideally over https).
Create a random string or guid (eg: http://php.net/manual/en/function.uniqid.php or http://www.lateralcode.com/creating-a-random-string-with-php/) , this is your token
Store the username / password in memcache with that token as a key
Set a timeout
$token = createToken("user1234", "pass2324");
print "Token: $token \n\n";
$credentials = credtialsFromToken($token);
print "Credentials from the token: ";
var_dump($credentials);
print "\n\n";
function setup() {
$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die ("Could not connect");
}
function createToken($user, $pass) {
$TOKEN_EXPIRE_TIME=60 * 60 * 24 * 30;
$credentials = array(
"user" => $user,
"pass" => $pass,
);
$token = uniqid( );
memcache_set($token, credentials, 'some variable', 0, 30);
return $token;
}
function credtialsFromToken($token) {
$credentials = memcache_get($token);
return $credentials;
}
If the token is incorrect or expired, they get an null credentials back and have to login.
Edit: cleaned it up into functions that appear to work in php...

Related

php REST - prevent direct access in browser

I'm developing REST API server in PHP, which I plan to call by client application, but I want to prevent direct API accesss via browser.
E.g. say I have GET call on "HOST/api/article/id" which would return article with given id to the client application. But when I type "HOST/api/article/id" in my browser, the article shouldn't be returned - I want nothing to happen (for example just return empty page or 403).
Is this possible?
If yes, is it common practice? (I mean is it something one would normally want to do or is it obscure and/or violates HTTP/REST principles and should be avoided?)
If yes, how do I do it? (in PHP/.htaccess/etc.)
I know I could implement some kind of authorization (like API key) instead which would allow API execution only for the client applications, which I plan to do anyway.
(I'm kind of new to this so maybe my question doesn't make sense. Maybe I misunderstood something very basic about how REST/HTTP/whatever works. If so, please tell me.)
The normal approach would be:
Your client (using a public key), requests token from the server, token checks if key is valid and not blacklisted (you can expire/blacklist old keys if they are compromised)
Token is sent every time
Server only responds if there is a token
Depending what your requirements, there is a hacky way to implement this.
Have a variable, called "my_client" with value true
On each request from your application sent the variable in your headers.
Server only servers information if "my_client" variable is in the headers
The cons with this approach is, that is not really secure, because each person can see the requests they make. Therefor can notice this extra information.
Its so simple that you can write it for a minute, just as a test.
<?php
if(!$_SERVER['HTTP_MY_CLIENT']){
header("HTTP/1.1 403 FORBIDEN");
}
Extending on the concept of using a header variable, we can use it as "semi token", which will mean we will populate the value with a random value that only we can read.
So the concept is this:
Client -> Request random value
Client /sets value in each request header/
Client -> makes requests to the server.
<?php
/* A basic API token and authentication class. */
class SimpleToken
{
/* Creates a salt based on the passed key that is good for the current day */
public static function generateSalt($key)
{
return md5($key . date('Y-m-d'));
}
/* Crytographically combine the key and the salt to produce a token */
public static function generateToken($key, $content)
{
$package = $content . $key;
return crypt($package);
}
/* Generate a relatively strong SSL key */
public static function generateKey()
{
$config = array(
"digest_alg" => "sha512",
"private_key_bits" => 4096,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
);
//Create a private key
$res = openssl_pkey_new($config);
//Extract the private part of the key
openssl_pkey_export($res, $private_key);
//Shorten it up for use in an API
return md5($private_key);
}
/* Verify the authenticity of the passed key/token pair */
public static function isAuthentic($key, $content, $token)
{
$package = $content . $key;
if(crypt($package, $token) == $token)
{
return true;
}
else
{
return false;
}
}
}

Twitter API responds with "Your credentials do not allow access to this resource" while calling statuses/update.json

I'm using Hybridauth 3 in my PHP app to make some periodical tweets on behalf of my account.
The app has all possible permissions. I'm giving it all permissions when it asks for them on the first auth step.
After that Twitter redirects me to the specified callback URL and there I'm getting a pair of access_token and access_token_secret.
But when I'm trying to make a tweet using these tokens - it gives me:
{"errors":[{"code":220,"message":"Your credentials do not allow access to this resource."}]}
Here's how I'm trying to make a tweet:
$config = [
'authentication_parameters' => [
//Location where to redirect users once they authenticate
'callback' => 'https://mysite/twittercallback/',
//Twitter application credentials
'keys' => [
'key' => 'xxx',
'secret' => 'yyy'
],
'authorize' => true
]
];
$adapter = new Hybridauth\Provider\Twitter($config['authentication_parameters']);
//Attempt to authenticate the user
$adapter->setAccessToken(/*tokens I've got from getAccessToken() on /twittercallback/*/);
if(! $adapter->isConnected()) {
// never goes here, so adapter is connected
return null;
}
try{
$response = $adapter->setUserStatus('Hello world!');
}
catch (\Exception $e) {
// here I've got the error
echo $e->getMessage();
return;
}
Tried to recreate tokens and key\secret pairs and passed auth process for the app many times, including entering password for my Twitter account (as suggested in some posts on stackoverflow) but still have this error.
P.S. According to this, Hybridauth has fixed the issue in the recent release.
It looks like you are using application authentication as opposed to user authentication. In order to post a tweet, you must authenticate as a user. Also, make sure your Twitter app has read/write privileges.
After comparing headers of outgoing requests from my server with the ones required by Twitter, I've noticed that Hybris doesn't add very important part of the header: oauth_token. At least it's not doing this in the code for Twitter adapter and for the scenario when you apply access token with setAccessToken(). It's just storing tokens in the inner storage but not initializing corresponding class member called consumerToken in OAuth1 class.
So to initialize the consumer token properly I've overridden the apiRequest method for Twitter class (before it used the defalut parent implementation) and added a small condition, so when consumer token is empty before the request - we need to try to init it.
public function apiRequest($url, $method = 'GET', $parameters = [], $headers = [])
{
if(empty($this->consumerToken)) {
$this->initialize();
}
return parent::apiRequest($url, $method, $parameters, $headers);
}
I'm not sure that I've fixed it the best way, but as long as it's working - that's fine.
For your info setAccessToken was fixed in v3.0.0-beta.2 (see PR https://github.com/hybridauth/hybridauth/pull/880)
I faced the same error when implementing a sample app in clojure and the following resource was a huge help to sort out my confusion about application-only auth vs user authentication: https://developer.twitter.com/en/docs/basics/authentication/overview/oauth

Slim framework - API Security

So I have a RESTful API, but I want to be safe so that not everyone can do anything.
$app->get('/users' , function(Request $request, Response $response){
$sql = "SELECT * FROM users";
try{
// Get db object
$db = new db();
// Connect
$db = $db->connect();
$stmt = $db->query($sql);
$users = $stmt->fetchAll(PDO::FETCH_OBJ);
$db = null;
echo json_encode($users);
} catch(PDOException $e){
echo '{"error": {"text": '.$e->getMessage().'}}';
}
});
So when i go to http://localhost/API/users i get all users into a json table.
Inside my database my data are stored like [{"id":"1","username":"werknemer","password":"...","level":"1","name":"piet","surname":"jan","email":"pietjan#gmail.nl"}]
I would like everyone to see his own table through my API and if you are level 5.
Is there a solution for that?
Your example is pretty basic and it's a starting point for using some "auth" concept in your REST APIs.
First things first: Authentication != Authorization.
Split these two concepts, the first one is going to make a user registered and logged into your app, the second one makes the "hard work" that you are looking for in this example, so check if a specific user is able to do some stuff.
For authentication, you can provide all the methods that you want, but remember that in REST your app MUST be stateless and you should provide a token (passed via HTTP Headers) that will be used by your application for understanding if the user is LOGGED and CAN do some stuff.
That's the key concept: A token (see JWT or OAUTH) should be used for authorization, and a very basic authorization is: "USER LOGGED".
In your example, you should use the middlewares for filter the http request, and don't enter into the router callback if the user is not authorized (logged in || have not a minLevel:5).
Checkout JWT or OAuth2 for this kinda stuff for more info.
Check this out -> (https://github.com/damianopetrungaro/slim-boilerplate) for a basic example of JWT generation in a slim app (if you are going to use this boilerplate PLEASE do not use the md5 for hash password this is a pretty basic example)
You need to add authentication and then authorisation to your API.
Authentication is the process of knowing who is accessing the API. A good way to do this is to you OAuth 2. I like and use Brent Shaffer's OAuth 2.0 Server library. https://github.com/akrabat/slim-bookshelf-api/tree/master/api contains an implementation of an API that using OAuth 2 to authorise users.
Once you know who is logging in, you then need to limit their access based on their role (or level). This is called access control. I like the zend components for this. Try zend-permissions-rbac - there's a good article on how to use it on the ZF blog.

Drupal 8 external/custom authentication provider

I am experimenting with Drupal 8 as our customer websites. Our customers authenticate through our own authentication application at the moment which speaks to our document store (instead of MySQL) to authenticate a user and provide them with a unique session ID (JWT eventually but that's another day and conversation) which we can use to then query a REST API and get user data in any of our other self apps.
We are moving over from an old JSP based websites to drupal as our apps are now written in Symfony 3 but want our customer websites to be Drupal 8.
Here's the bit I am trying to work out. If I authenticate in our old website I want to be able to redirect to the Drupal 8 website with the session ID we have in our hand and use that to fetch a object back of our logged in user. I have this bit working fine but I now am in a position to say... Ok I have the user object back, the 3rd party service has said that session ID is valid so we know we are authenticated.
Please refer to the below flow chart. I want to be able to also authenticate in Drupal 8 manually. Is this possible (I am sure it is) and if so can someone point me in the right direction as to what I need/should be doing, API's I should be calling?
Thank you kindly and good day :)
You should use the External Auth module.
A good exemple of how use this module is the SimpleSamlPHP Auth
Ok so it turned out not to be that tricky in the end. I thought I would have to extend and implement various class and create my own provider (which is probably the best practice) but for KISS sake I found another way.
Create a user first if one does not exists based on the user data I get back from my external service. Then pass that created user to the user_login_finalize method (why are a lot of methods underscored Drupal...) which then authenticated my user.
public function inbound(Request $request)
{
// Point the guzzle client to our external session service.
$client = new GuzzleHttpClient([
'base_uri' => 'https://myexternalservice.com/apps/authentication/2/',
]);
// Attempt to send to request with the session ID from the parameters.
try {
$response = $client->request('GET', 'api/v1/user/' . $request->get('session_id'));
} catch (\Exception $e) {
throw new \HttpException($e->getMessage());
}
// Convert the response to an array.
$result = json_decode((string) $response->getBody(), true);
// Convert our array to a user entity.
if ($user = $this->convertResponseToUser($result['user'])) {
try {
// Attempt to load the user. If the user does not exist then create them first.
if (!$assumeUser = user_load_by_mail($user->getEmail())) {
// Create a Drupal user object.
$assumeUser = $this->createUser([
'name' => $user->getFirstName() . ' ' . $user->getLastName(),
'mail' => $user->getEmail()
]);
$assumeUser->save();
}
// Authenticate the user.
user_login_finalize($assumeUser);
} catch (\Exception $e) {
drupal_set_message(t('An unhandled exception occurred during authentication.'), 'error');
return $this->redirect('user.login');
}
}
return $this->redirect('mymodule.route');
}

How do I implement single authentication in a RESTful app?

I can't seem to grasp how authentication works for REST using Zend Framework. I want the user to be able to login once and then allow him to access any area of the site depending on his user level. We have a working non-REST login code, I just basically copied it and placed in postAction() method of the Api_LoginController class.
class Api_LoginController extends REST_Controller
{
public function postAction() {
$request = $this->getRequest();
$form = new Application_Form_LoginForm;
if ($request->isPost()) {
if ($form->isValid($request->getPost())) {
$email = $form->getValue('email');
$password = $form->getValue('password');
$user = Application_Model_UserModel::getUserByEmail($email);
if ( $user && $user->login($password)) {
echo json_encode(array('error' => false, 'message' => 'Logged in.'));
} else {
echo json_encode(array('error' => true, 'message' => 'Login failed.'));
}
}
}
}
}
How do I go from here? Should Zend(1.11) be able to identify the user in the subsequent access? Right now, it appears it can't identify the same user after login.
It is possible to provide authentication in a REST application using Zend.
This is roughly the idea behind it:
A User logs in using his username/password
In your Application you validate the login credentials
Using these credentials you can generate an unique token, similar to a session token in a normal application
You send back the identification token to the User
Each subsequent request the User has to provide his token, as proof he's logged in
Your Application will validate this token and do the required action
Zend contains a lot of this stuff already, with a bit of searching you'll find a lot of functionality that might provide some of these points and make them fit in your application.

Categories