In-app billing verification. Does server side code example exist somewhere? - android

A lot of people ask how to write server side code
for in-app billing verificartion.
Can anybody publish such code? Or know where such code is.
How to install in on the server?
There are similar subjects
I could not understand it.
I don't know php.
Is it the next nightmare which I must study?
Thanks for help and advices.

Actually it's pretty easy, you just need a small function like this in PHP:
function checkPayment($data, $signature)
{
$base64EncodedPublicKey = "yourBase64PublicKey";
$openSslFriendlyKey = "-----BEGIN PUBLIC KEY-----\n" . chunk_split($base64EncodedPublicKey, 64, "\n") . "-----END PUBLIC KEY-----";
$publicKeyId = openssl_get_publickey($openSslFriendlyKey);
$result = openssl_verify ($data, base64_decode($signature), $publicKeyId, OPENSSL_ALGO_SHA1);
/*
if ($result == 1) {
echo "Success";
} elseif ($result == 0) {
echo "Verification Failed";
}
*/
return $result;
}

Here is (uncompleted) python example:
from M2Crypto import BIO, RSA, EVP
ANDROID_PK = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgmZW0GxWr0v1ndLfxHbV2ruWcmQ
<some lines skipped>
cwWjx5sWSahVp0M5aYRysSkGGjSxe1wIDAQAB
-----END PUBLIC KEY-----"""
def validate_google_play_signature(signed_data, signature_base64, public_key):
# http://stackoverflow.com/a/546476/227024
bio = BIO.MemoryBuffer(public_key)
rsa = RSA.load_pub_key_bio(bio)
pubkey = EVP.PKey()
pubkey.assign_rsa(rsa)
pubkey.reset_context(md="sha1")
pubkey.verify_init()
pubkey.verify_update(signed_data)
signature = base64.b64decode(signature_base64)
return bool(pubkey.verify_final(signature))

Related

Android TV Remote Control API [duplicate]

I have been tasked to create an application for android mobile to control an Android TV, preferably the dashboard/landingpage outside of any apps (settings included).
It doesn't really matter if it's via bluetooth or wifi, although I have found that bluetooth is not possible as the HID profile is needed, and that profile is only available on API 28 (I need to support from API 19 up)
There are some apps on the play store that already have this functionality. Most connect via Wifi to the Android TV, also pairing with it.
By analysing the APK Files I found out some options, i.e.
some use the
connectSDK library
others use what seems to be a native google package that I can't seem to find
import com.google.android.tv.support.remote.Discovery;
import com.google.android.tv.support.remote.core.Client;
import com.google.android.tv.remote.BuildInfo;
I found that a couple of years ago the Anymote Protocol could be used as well, but that one only works with Google TV, not Android TV.
The problems I am facing right now is that the connectSDK library isn't being maintained and does not contain any code for Android TV connections.
The native google package cannot be found anywhere, not sure if it's included in a specific Jar file, or maybe some obscured/hidden dependency?
I could try to create a connection to a specific socket with Android TV, I know for example that the ServiceType is "_androidtvremote._tcp." and that the port number is 6466. But I'm not sure what would be the best way to implement this.
What I'm looking for are some pointers or ideas how I could tackle this problem. Maybe some references as well.
EDIT on December 2021: I created a new documentation for the new protocol v2.
EDIT on September 2021: Google is deploying a new version of the "Android TV Remote Control" (from v4.x to v5), and this version is not compatible with the legacy pairing system. For now it's necessary to keep a version < 5 to make it work.
We spent some time to find how to connect and control an Android/Google TV (by reverse engineering), and I'm sharing here the result of our findings. For a more recent/updated version, you can check this wiki page.
I develop in PHP so I'll share the code in PHP (the Java code can be found by decompiling some Android apps using https://github.com/skylot/jadx)
Thanks to #hubertlejaune for his tremendous help.
The Android TV (aka server in this document) should have 2 open ports: 6466 and 6467.
To know more about the Android TV, we can enter the below Linux command:
openssl s_client -connect SERVER_IP:6467 -prexit -state -debug
Which will return some information, including the server's public certificate.
If you only want the server's public certificate:
openssl s_client -showcerts -connect SERVER_IP:6467 </dev/null 2>/dev/null|openssl x509 -outform PEM > server.pem
Pairing
The pairing protocol will happen on port 6467.
Client's certificate
It's required to generate our own (client) certificate.
In PHP we can do it with the below code:
<?php
// the commande line is: php generate_key.php > client.pem
// certificate details (Distinguished Name)
// (OpenSSL applies defaults to missing fields)
$dn = array(
"commonName" => "atvremote",
"countryName" => "US",
"stateOrProvinceName" => "California",
"localityName" => "Montain View",
"organizationName" => "Google Inc.",
"organizationalUnitName" => "Android",
"emailAddress" => "example#google.com"
);
// create certificate which is valid for ~10 years
$privkey = openssl_pkey_new();
$cert = openssl_csr_new($dn, $privkey);
$cert = openssl_csr_sign($cert, null, $privkey, 3650);
// export public key
openssl_x509_export($cert, $out);
echo $out;
// export private key
$passphrase = null;
openssl_pkey_export($privkey, $out, $passphrase);
echo $out;
It will generate a file called client.pem that contains both the public and the private keys for our client.
Connection to the server
You need to open a TLS/SSL connection to the server using port 6467.
In PHP, you could use https://github.com/reactphp/socket:
<?php
use React\EventLoop\Factory;
use React\Socket\Connector;
use React\Socket\SecureConnector;
use React\Socket\ConnectionInterface;
require __DIR__ . '/./vendor/autoload.php';
$host = 'SERVER_IP';
$loop = Factory::create();
$tcpConnector = new React\Socket\TcpConnector($loop);
$dnsResolverFactory = new React\Dns\Resolver\Factory();
$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
$dnsConnector = new React\Socket\DnsConnector($tcpConnector, $dns);
$connector = new SecureConnector($dnsConnector, $loop, array(
'allow_self_signed' => true,
'verify_peer' => false,
'verify_peer_name' => false,
'dns' => false,
'local_cert' => 'client.pem'
));
$connector->connect('tls://' . $host . ':6467')->then(function (ConnectionInterface $connection) use ($host) {
$connection->on('data', function ($data) use ($connection) {
$dataLen = strlen($data);
echo "data recv => ".$data." (".strlen($data).")\n";
// deal with the messages received from the server
});
// below we can send the first message
$connection->write(/* first message here */);
}, 'printf');
$loop->run();
?>
Protocol
⚠️ Attention, each message is sent as a JSON string, but with two components/parts:
(first) we send the length of the message (JSON string) on 4 bytes,
(second) we send the message (JSON string) itself.
PAIRING_REQUEST(10)
As soon as we are connected to the server, we send a PAIRING_REQUEST(10) message (type = 10).
The first message to send is:
{"protocol_version":1,"payload":{"service_name":"androidtvremote","client_name":"CLIENT_NAME"},"type":10,"status":200}
The server returns a PAIRING_REQUEST_ACK(11) message with type is 11 and status is 200:
{"protocol_version":1,"payload":{},"type":11,"status":200}
OPTIONS(20)
Then the client replies with a OPTIONS(20) message (type = 20):
{"protocol_version":1,"payload":{"output_encodings":[{"symbol_length":4,"type":3}],"input_encodings":[{"symbol_length":4,"type":3}],"preferred_role":1},"type":20,"status":200}
The server returns a OPTIONS(20) message with type is 20 and status is 200.
CONFIGURATION(30)
Then the client replies with a CONFIGURATION(30) message (type = 30):
{"protocol_version":1,"payload":{"encoding":{"symbol_length":4,"type":3},"client_role":1},"type":30,"status":200}
The server returns a CONFIGURATION_ACK(31) message with type is 31 and status is 200.
🎉 The code appears on the TV screen!
SECRET(40)
Then the client replies with a SECRET(40) message (type = 40):
{"protocol_version":1,"payload":{"secret":"encodedSecret"},"type":40,"status":200}
At this stage, the TV screen shows a code with 4 characters (e.g. 4D35).
To find the encodedSecret:
we use a SHA-256 hash;
we add the client public key's modulus to the hash;
we add the client public key's exponent to the hash;
we add the server public key's modulus to the hash;
we add the server public key's exponent to the hash;
we add the last 2 characters of the code to the hash (in the example it's 35).
The result of the hash is then encoded in base64.
The server returns a SECRET_ACK(41) message with type is 41 and status is 200, as well as an encoded secret that permits to verify – we didn't try to decode it, but it's probably the first 2 characters of the code:
{"protocol_version":1,"payload":{"secret":"encodedSecretAck"},"type":41,"status":200}
PHP Code
(you can find some Java code that produces pretty much the same)
Here is the related PHP code:
<?php
use React\EventLoop\Factory;
use React\Socket\Connector;
use React\Socket\SecureConnector;
use React\Socket\ConnectionInterface;
require __DIR__ . '/./vendor/autoload.php';
$host = 'SERVER_IP';
$loop = Factory::create();
$tcpConnector = new React\Socket\TcpConnector($loop);
$dnsResolverFactory = new React\Dns\Resolver\Factory();
$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
$dnsConnector = new React\Socket\DnsConnector($tcpConnector, $dns);
// get the server's public certificate
exec("openssl s_client -showcerts -connect ".escapeshellcmd($host).":6467 </dev/null 2>/dev/null|openssl x509 -outform PEM > server.pem");
$connector = new SecureConnector($dnsConnector, $loop, array(
'allow_self_signed' => true,
'verify_peer' => false,
'verify_peer_name' => false,
'dns' => false,
'local_cert' => 'client.pem'
));
// return the message's length on 4 bytes
function getLen($len) {
return chr($len>>24 & 0xFF).chr($len>>16 & 0xFF).chr($len>>8 & 0xFF).chr($len & 0xFF);
}
// connect to the server
$connector->connect('tls://' . $host . ':6467')->then(function (ConnectionInterface $connection) use ($host) {
$connection->on('data', function ($data) use ($connection) {
$dataLen = strlen($data);
echo "data recv => ".$data." (".strlen($data).")\n";
// the first response from the server is the message's size on 4 bytes (that looks like a char to convert to decimal) – we can ignore it
// only look at messages longer than 4 bytes
if ($dataLen > 4) {
// decode the JSON string
$res = json_decode($data);
// check the status is 200
if ($res->status === 200) {
// check at which step we are
switch($res->type) {
case 11:{
// message to send:
// {"protocol_version":1,"payload":{"output_encodings":[{"symbol_length":4,"type":3}],"input_encodings":[{"symbol_length":4,"type":3}],"preferred_role":1},"type":20,"status":200}
$json = new stdClass();
$json->protocol_version = 1;
$json->payload = new stdClass();
$json->payload->output_encodings = [];
$encoding = new stdClass();
$encoding->symbol_length = 4;
$encoding->type = 3;
array_push($json->payload->output_encodings, $encoding);
$json->payload->input_encodings = [];
$encoding = new stdClass();
$encoding->symbol_length = 4;
$encoding->type = 3;
array_push($json->payload->input_encodings, $encoding);
$json->payload->preferred_role = 1;
$json->type = 20;
$json->status = 200;
$payload = json_encode($json);
$payloadLen = strlen($payload);
$connection->write(getLen($payloadLen));
$connection->write($payload);
break;
}
case 20:{
// message to send:
// {"protocol_version":1,"payload":{"encoding":{"symbol_length":4,"type":3},"client_role":1},"type":30,"status":200}
$json = new stdClass();
$json->protocol_version = 1;
$json->payload = new stdClass();
$json->payload->encoding = new stdClass();
$json->payload->encoding->symbol_length = 4;
$json->payload->encoding->type = 3;
$json->payload->client_role = 1;
$json->type = 30;
$json->status = 200;
$payload = json_encode($json);
$payloadLen = strlen($payload);
$connection->write(getLen($payloadLen));
$connection->write($payload);
break;
}
case 31:{
// when we arrive here, the TV screen displays a code with 4 characters
// message to send:
// {"protocol_version":1,"payload":{"secret":"encodedSecret"},"type":40,"status":200}
$json = new stdClass();
$json->protocol_version = 1;
$json->payload = new stdClass();
// get the code... here we'll let the user to enter it in the console
$code = readline("Code: ");
// get the client's certificate
$clientPub = openssl_get_publickey(file_get_contents("client.pem"));
$clientPubDetails = openssl_pkey_get_details($clientPub);
// get the server's certificate
$serverPub = openssl_get_publickey(file_get_contents("public.key"));
$serverPubDetails = openssl_pkey_get_details($serverPub);
// get the client's certificate modulus
$clientModulus = $clientPubDetails['rsa']['n'];
// get the client's certificate exponent
$clientExponent = $clientPubDetails['rsa']['e'];
// get the server's certificate modulus
$serverModulus = $serverPubDetails['rsa']['n'];
// get the server's certificate exponent
$serverExponent = $serverPubDetails['rsa']['e'];
// use SHA-256
$ctxHash = hash_init('sha256');
hash_update($ctxHash, $clientModulus);
hash_update($ctxHash, $clientExponent);
hash_update($ctxHash, $serverModulus);
hash_update($ctxHash, $serverExponent);
// only keep the last two characters of the code
$codeBin = hex2bin(substr($code, 2));
hash_update($ctxHash, $codeBin);
$alpha = hash_final($ctxHash, true);
// encode in base64
$json->payload->secret = base64_encode($alpha);
$json->type = 40;
$json->status = 200;
$payload = json_encode($json);
$payloadLen = strlen($payload);
$connection->write(getLen($payloadLen));
$connection->write($payload);
break;
}
}
}
}
});
// send the first message to the server
// {"protocol_version":1,"payload":{"service_name":"androidtvremote","client_name":"TEST"},"type":10,"status":200}
$json = new stdClass();
$json->protocol_version = 1;
$json->payload = new stdClass();
$json->payload->service_name = "androidtvremote";
$json->payload->client_name = "interface Web";
$json->type = 10;
$json->status = 200;
$payload = json_encode($json);
$payloadLen = strlen($payload);
// send the message size
$connection->write(getLen($payloadLen));
// send the message
$connection->write($payload);
}, 'printf');
$loop->run();
?>
Send Commands
Now that the client is paired with the server, we'll use port 6466 to send the commands.
Please, note we'll use an array of bytes for the commands.
Configuration message
An initial message must be sent:
[1,0,0,21,0,0,0,1,0,0,0,1,32,3,0,0,0,0,0,0,4,116,101,115,116]
The server will respond with an array of bytes that should start with [1,7,0
Commands
You must send two messages to execute one command.
The format is:
[1,2,0,{SIZE=16},0,0,0,0,0,0,0, {COUNTER} ,0,0,0, {PRESS=0} ,0,0,0,{KEYCODE}]
[1,2,0,{SIZE=16},0,0,0,0,0,0,0,{COUNTER+1},0,0,0,{RELEASE=1},0,0,0,{KEYCODE}]
The {KEYCODE} can be found on https://developer.android.com/reference/android/view/KeyEvent.
For example, if we want to send a VOLUME_UP:
[1,2,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24]
[1,2,0,16,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,24]
PHP Code
And here some PHP code:
<?php
use React\EventLoop\Factory;
use React\Socket\Connector;
use React\Socket\SecureConnector;
use React\Socket\ConnectionInterface;
require __DIR__ . '/./vendor/autoload.php';
$host = 'SERVER_IP';
$loop = Factory::create();
$tcpConnector = new React\Socket\TcpConnector($loop);
$dnsResolverFactory = new React\Dns\Resolver\Factory();
$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
$dnsConnector = new React\Socket\DnsConnector($tcpConnector, $dns);
$connector = new SecureConnector($dnsConnector, $loop, array(
'allow_self_signed' => true,
'verify_peer' => false,
'verify_peer_name' => false,
'dns' => false,
'local_cert' => 'client.pem'
));
// convert the array of bytes
function toMsg($arr) {
$chars = array_map("chr", $arr);
return join($chars);
}
// connect to the server
$connector->connect('tls://' . $host . ':6466')->then(function (ConnectionInterface $connection) use ($host) {
$connection->on('data', function ($data) use ($connection) {
// convert the data received to an array of bytes
$dataLen = strlen($data);
$arr = [];
for ($i=0; $i<$dataLen;$i++) {
$arr[] = ord($data[$i]);
}
$str = "[".implode(",", $arr)."]";
echo "data recv => ".$data." ".$str." (".strlen($data).")\n";
// if we receive [1,20,0,0] it means the server sent a ping
if (strpos($str, "[1,20,0,0]") === 0) {
// we can reply with a PONG [1,21,0,0] if we want
// $connection->write(toMsg([1,21,0,0]));
}
else if (strpos($str, "[1,7,0,") === 0) {
// we can send the command, here it's a VOLUME_UP
$connection->write(toMsg([1,2,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24]));
$connection->write(toMsg([1,2,0,16,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,24]));
}
});
// send the first message (configuration) to the server
$arr = [1,0,0,21,0,0,0,1,0,0,0,1,32,3,0,0,0,0,0,0,4,116,101,115,116];
$connection->write(toMsg($arr));
}, 'printf');
$loop->run();
?>
So, I found the answer what I was looking for.
If you are a Google Partner (and only then), and have an account with those privileges, you can simply download the jar file at this location. Documentation can be found there as well and the SDK exists for Android and iOS.
Not much information is available how to use it. But by looking over the different classes it can become clear.

How to put timestamp in android generated crypto signature

What I have is private key without header, footer and spaces (it's test one)
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg0m4yLz+sdzZtBG9Q3HQ9++wcfq1O4hOWgSBMb/A6eijyhRANCAAQeB0fBl2D7HZOKVBjpPiU2jabzNxQU4ZYrJ+MSA3LpzZxmRk2JaFHNujjkJghQT19HHjg3Fnkb8Y9oIhB9neXBI
And this code in android which generates signature in required format.
val signatureSHA256 = Signature.getInstance("SHA256withECDSA")
val encoded = Base64.decode(privateKeyHere, Base64.DEFAULT)
val privateKey: PrivateKey = KeyFactory.getInstance("EC").generatePrivate(PKCS8EncodedKeySpec(encoded))
signatureSHA256.initSign(privateKey)
val finalSignature = signatureSHA256.sign().toHexString()
//Somewhere
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
And function which return timestamp:
fun getXTimestamp(): Long {
return (System.currentTimeMillis() / 1000L)
}
From android Im getting finalSignature and getXTimestamp()
And I need to verify my signature in php script:
$timestamp = '1625730735';
$public_key = "
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHgdHwZdg+x2TilQY6T4lNo2m8zcU
FGWKyfjEgNy6c2cZkZNiWhRzbo45CYIUE9fRx44NxZ5G/GPaCIQfZ3lwSA==
-----END PUBLIC KEY-----
";
//hex2bin("finalSignature")
$sign = hex2bin("3045022048011d511094a5270c528ca5064b07084e36ccfd2ee3f5e1e20278fb5d83cdba022100d71a0096ef2c6288554a51017a89374b18c7e84ba7031a43d67f53d7ce89152c");
$result = openssl_verify($timestamp, $sign, $public_key, OPENSSL_ALGO_SHA256);
print $result;
Now php script launched from console returns 0 but should return 1.
I think I should somehow update or put timestamp in signature.
I tried to put it in update() as bytes, but still got 0
Who can help pls?)
On PHP side ("verification") you use your 'timestamp' as text string as input for your openssl_verify function.
On Kotlin-side you need to do the same - get the timestamp as string and use it as input for a sign.update call with [Pseudo-code] timestamp-string.getBytes(StandardCharset.UTF8) as input.
As I'm not familiar with Kotlin I'm using the code in the comment of #Andrej Kijonok as it solves the problem :-)
val signatureTimestamp = getXTimestamp().toString().toByteArray(Charsets.UTF_8)
signatureSHA256.initSign(privateKey)
signatureSHA256.update(signatureTimestamp)

Authorization Credentials sparql

I am newbie in sparql queries. I try to send a sparql query from my android device to an EndPointURI remote server so i can get the results. I want to select all the results that match with "salmon", as it seems in my code below.
My issue is in this line: "ResultSet resultSet = qe.execSelect();".
When i try to take the results i get
no authentication challenges found at libcore.net.http.httpURLConnectionImpl.get Authorization Credentials
and this because myURL wants authentication credentials and i dont know how to send them.
Any ideas of how can i send username and password succesfully?
My imports are :
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.Syntax;
the .jars in my libs file are:
androjena_0.5,
arqoid_0.5,
icu4j_3_4,
iri-0.8,
lucenoid_3.0.2,
slf4j-android-1.6.1-RC1
and finally my function
public String queryRemoteSparqlEndpoint()
{
String username="lala";
String password="lala";
String queryString = "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> select ?sp"
+where {"
+"<URL>"
+"?sp \"salmon\"."
+"}";
String sparqlEndpointUri = "myURL/sparql";
Query query = QueryFactory.create(queryString, Syntax.syntaxARQ);
query.setLimit(10);
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password.toCharArray());
}
});
QueryExecution qe = QueryExecutionFactory.sparqlService(sparqlEndpointUri, query);
ResultSet resultSet = qe.execSelect();
while (resultSet.hasNext())
{
//.....
}
return results.toString();
}
Thank you in advance.
I don't know which version of Jena is being run in androjena 0.5 but the current version of Jena has support for
sparqlService(String service, Query query, HttpAuthenticator authenticator)
where HttpAuthenticator is from Apache HTTP Client. I don't know if this works on androjena.
If the code does not support it, you can make the call directly with native HTTP operations and pass the resulting input stream to a result set parser (see ResultSetFactory).

Acra post script

Tried to use ACRA in my app, but I can't get the google drive or the email working (in google drive I don't know how to create the form from the template, email tells me there is no such email address even though I am sending from the same email address). I would rather get the google drive spreadsheet thing working or better yet - if there is a ready-to-use script for free web host that I can use to get the reports. Anyone knows of such a script?
EDIT: I need a php one...
Looks like ACRA uses a simple post. What language do you need it in? For asp.net:
protected void Page_Load(object sender, EventArgs e)
{
StringBuilder err = new StringBuilder();
foreach (string name in Request.Form)
{
err.AppendLine(name + ": " + Request.Form[name]);
}
TextWriter tw = null;
try
{
tw = new StreamWriter("f:\\errorLogs\\error_" + DateTime.Now.Ticks + ".txt");
tw.WriteLine(err.ToString());
}
catch (Exception) { }
finally
{
if(tw != null)
tw.Close();
}
}
Since you are using a "free web host", the location probably isn't valid for you, but this should at least get you pointed in the right direction. There is another post which is similar to yours. Most free web hosts use php.
EDIT:
Here is a basic PHP script (Not tested, but seems OK to me. My PHP is a bit rusty):
<?php
$file = 'postData.txt';
$arr= $_POST;
$fp = fopen($file, 'w') or die('Could not open file!');
foreach ($arr as $key => $value) {
$toFile = "Key: $key; Value: $value \n";
// write to file
fwrite($fp, "$toFile") or die('Could not write to file');
}
// close file
fclose($fp);
?>

Create/Login User from a mobile device using Symfony2 and FOSUserBundle

Firstly, I want to create a user sending a post-request from my android app to the server, which uses Symfony2 and the FOSUserBundle.
Finally, I want to login a user from the mobile app and then communicate data with the server.
I know how to implement a post-request on the android-device. But I don't know how I need to configure the FOSUserBundle and security.yml etc to fit my needs. Although I might need a _csrf_token or something and I dont know where to get it from.
I already changed the authentication method from form_login to http_basic and think that this will be the easiest way of doing the authentication (using https to secure the passwords).
But now.. what do I need to do, to achieve the creating and logging in actions without forms? What do I need to put in the post-request on the mobile device?
Thanks for any ideas, comments and solutions!!
A late answer, but it might help.
I'm working on a similar situation and I got this:
In security.yml
security:
providers:
fos_userbundle:
id: fos_user.user_manager
firewalls:
main:
pattern: ^/
stateless: true
http_basic:
realm: "API"
access_control:
- { path: /, role: ROLE_USER }
role_hierarchy:
ROLE_OWNER: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
In config.yml:
fos_user:
db_driver: orm
firewall_name: main
user_class: <your user class>
In my test-method:
Reference: Authentication for a Symfony2 api (for mobile app use)
public function testAuthentication()
{
$client = $this->createClient();
// not authenticated
$client->request('GET', '<url>');
$this->assertEquals(401, $client->getResponse()->getStatusCode());
// authenticated
$client->request('GET', '<url>', array(), array(), array(
'PHP_AUTH_USER' => '<username from your database>',
'PHP_AUTH_PW' => '<password>'
));
$this->assertEquals(200, $client->getResponse()->getStatusCode());
}
For communication with that API, I'd suggest cURL or Buzz
Hope this helps!
Cheers,
Dieter
I had the same problem but I found the solution for registration : (the user enter the username , email and password)
In the UserController of your UserBundle (src/Project/UserBundle/Controller/DefaultController)
define a new function registerAction():
public function registerAction()
{
$user = new User();
$request = $this->getRequest();
$username = $request->request->get('username');
$password= $request->request->get('password');
$email= $request->request->get('email');
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($password, $user->getSalt());
$user->setPassword($password);
$user->setUsername($username);
$user->setUsernameCanonical($username);
$user->setEmail($email);
$user->setEmailCanonical($email);
$user->setEnabled(true);
$user->setLocked(false);
$user->setExpired(false);
$user->setCredentialsExpired(false);
$em = $this->get('doctrine')->getEntityManager();
$em->persist($user);
$em->flush();
/* $response = new Response(json_encode(array('user' => $tes)));
$response->headers->set('Content-Type', 'application/json');
return $response;*/
return new JsonResponse('good');
}
}
and don't forgot to import :
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder;
use Telifoon\UserBundle\Entity\User;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
in UserBundle/Resources/config/routing.yml add follwoing route:
inscription_post:
pattern: /v1/api/register
defaults: { _controller: ProjectUserBundle:Default:register }
requirements:
_method: POST
My entity ( src/Project/UserBUndle/Entity/User) is :
use FOS\UserBundle\Model\User as BaseUser;
/**
* User
*/
class User extends BaseUser
{
public function __construct()
{
parent::__construct();
// your own logic
}
}
If test the user is added correctely to my database :)

Categories

Resources