How to Firebase Auth with Rails? - android

I have fully integrated Firebase Auth in my Android App, now I want the client to interact with my backend (rails) using a unique Token. my question is this how it's done :
User is logged-in, using for example Facebook Auth,
Send the Firebase Token to backend and verify it
Respond to the client with a unique Token for the upcoming Api requests (get users data, save user data ...)
Thank you

I have developed the firebase_id_token gem.
It deals with all the certificates/signature business.
It can be used simply as:
FirebaseIdToken::Signature.verify(token)

Taking JinLiu's answer forward , once you get the Token in your android code , send it to your backend say : https://www.yourbackend.com?authenticate?token=asdad7687h...
Note that the token generated by Firebase is a JWT token , we need to first verify its authenticity and decode it in the backend. For this , you need to add these two gems to your gemfile
gem 'jwt' , '1.5.6'
gem 'rest-client' , '2.0.1'
With that done , you need to add these two (private) function in your controller:
def get_set_user
begin
token = params[:token]
if token.nil?
#user = nil
return
end
firebase_id = verify_user(token)[0]["user_id"]
if User.where(:firebase_id => firebase_id).exists?
#user = User.where(:firebase_id => firebase_id).first
else
#user = User.new(:firebase_id => firebase_id)
#user.save
#user
end
rescue
#user = nil
end
end
def verify_user(token)
certificate_url = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com"
myresponse = RestClient.get(certificate_url).body
certificates = JSON.parse myresponse.gsub('=>', ':')
myjson =""
certificates.each do|key , value|
begin
x509 = OpenSSL::X509::Certificate.new(value)
iss = 'https://securetoken.google.com/<yourdomain>'
aud = 'yourdomain' # change this
myjson = JWT.decode(token, x509.public_key, true,
{ algorithm: "RS256", verify_iat: true ,
iss: iss , verify_iss: true ,
aud: aud , verify_aud: true
})
return myjson
rescue
end
end
return nil
end
Now you can call get_set_user as before action to see , if the user has valid token .
The idea is simple. Check if the token is signed by one of the keys mentioned at https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com . If yes , decode the token and get the firebase id .

You do not need step #3. Firebase Auth Android SDK getToken() API returns a short-lived (one hour) Firebase Auth ID Token for the login user, and automatically returns a new token if the previous one expires.
The Firebase token verification doc describes how to get a Firebase token on client app and verify the token on server side.

Related

Google login from android And Access auth token for server

I am trying to generate server auth code in android
gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestScopes(new Scope(Scopes.DRIVE_APPFOLDER))
.requestServerAuthCode(getString(R.string.server_client_id), false)
.build();
Then I have tried to get server auth code like this.
result.requestServerAuthCode(getString(R.string.server_client_id), false)
Suppose i got a auth token like 'bla bla bla';
Then using laravel socialite I am trying to get user on the server side
Socialize::driver('google')->userFromToken('bla bla bla')
It shows me error
GuzzleHttp\Exception\ClientException with message 'Client error: GET https://www.googleapis.com/plus/v1/people/me?prettyPrint=false resulted in a 401 Unauthorized` response:
{"error":{"errors":[{"domain":"global","reason":"authError","message":"Invalid Credentials","locationType":"header","loc (truncated...)
Actually the code sent by google on android is not the access token to get access token you can do this on laravel controller.
Install this composer library https://github.com/pulkitjalan/google-apiclient
$client = new \PulkitJalan\Google\Client(['client_id' => 'YOUR_CLIENT_ID', 'client_secret' => 'YOUR_SECRET', 'redirect_uri' => 'YOUR_REDIRECT_URI', 'developer_key' => 'YOUR_KEY']);
$google = $client->getClient();
$google->authenticate($token);
$access_token = $client->getAccessToken()["access_token"];
//and now here you go
$user = Socialize::driver('google')->userFromToken($access_token);
The easiest way is to use the Google_Client class that is provided from Google. you can find it here
Now, you've got two option:
You can send an id_token to the server, and validate it just as the link said and then just fetch the user info from the object received.
You can send the backend_auth_code to the server instead of the id_token, and then use the method fetchAccessTokenWithAuthCode($code) of the Google_Client class. This gives you the access token, id token and other stuff. You can then use that access_token with Laravel Socialite
$client = new Google_Client([
'client_id' => config('services.google.client_id'),
'client_secret' => config('services.google.client_secret')
]);
$data = $client->fetchAccessTokenWithAuthCode($code);
$user = Socialite::driver('google')->scopes(['profile','email'])->userFromToken($data['access_token']);

JWT: Authentication in slim v3 and Android

I am using Slim framework to return JSON to my Android device. I am currently working on login on my device. I am using 3 different ways to login: Facebook, Google and account login. When he takes account login he can register a new account or login with an existing one.
For security on my web service I thought to use JWT security. So I am reading and watching video's about how it works. I think I understand how it works, but I cannot find anything about how to implement it correctly.
The middleware I use for slim v3 is called: Slim-JWT-Auth.
I found the following link to implement this in my slim framework, and it works correctly I think.
Now my questions:
How do I generate my Token?
When do I generate my Token?
Do I also need a Token when using Google or Facebook sign-in? because they already use a Auth2.0 token?
I understand how it works but nobody is talking about when and how to implement it. So when do I need to generate the token (on login on the webservice?), and do I need to generate a token after every start of the app, or do I just need to wait until the token expires?
How do I generate my Token?
Since the middleware already includes firebase/php-jwt library you can use it to generate the token.
$now = new DateTime();
$future = new DateTime("now +2 hours");
$server = $request->getServerParams();
$payload = [
"iat" => $now->getTimeStamp(),
"exp" => $future->getTimeStamp(),
"sub" => $server["PHP_AUTH_USER"]
];
$secret = "supersecretkeyyoushouldnotcommittogithub";
$token = JWT::encode($payload, $secret, "HS256");
When do I generate my Token?
In your api you can for example include a password protected route which returns the token. All other routes except /token are JWT authenticated. Client can request token with every request or just always bit before the old one expires.
$app->add(new \Slim\Middleware\HttpBasicAuthentication([
"path" => "/token",
"users" => [
"test" => "test"
]
]);
$app->add(new \Slim\Middleware\JwtAuthentication([
"secret" => "supersecretkeyyoushouldnotcommittogithub"
"rules" => [
new RequestPathRule([
"path" => "/",
"passthrough" => ["/token"]
])
]
]);
$app->post("/token", function ($request, $response, $arguments) {
$now = new DateTime();
$future = new DateTime("now +2 hours");
$server = $request->getServerParams();
$payload = [
"iat" => $now->getTimeStamp(),
"exp" => $future->getTimeStamp(),
"sub" => $server["PHP_AUTH_USER"],
];
$secret = "supersecretkeyyoushouldnotcommittogithub";
$token = JWT::encode($payload, $secret, "HS256");
$data["status"] = "ok";
$data["token"] = $token;
return $response->withStatus(201)
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
});
Do I also need a Token when using Google or Facebook sign-in? because they already use a Auth2.0 token?
There is no clear answer to this. It "depends". You could for example authenticate your /token route with Facebook or Google and return your own JWT token from there.
There is an work in progress more detailed example implementation of everything above you might want to check.

django - preventing from two clients to login as the same user

I have a login/logout module in my django app (using DRF).
it works in token authentication - when user logs in he passes a username and a password and gets a token, that can be used forever.
(I save the token in my client app).
when he logs out - I delete the token from the client app.
The problem I noticed is that when one client (android app) in logged in with user1 for example (currently has the token that was achieved from the server), other clients can login as the same user (provide same username and password and get the token) - and now I have both clients logged in as user1.
Here is the django code for getting the token:
class ObtainAuthTokenAndUser(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
user_serializer = UserSerializer(user)
return Response({'token': token.key, 'user': user_serializer.data})
obtain_auth_token_and_user = ObtainAuthTokenAndUser.as_view()
What can I do in order to prevent this situation??
In the case of another client tries to log in with a already logged in user - I want to send a "already logged in from another device" message and a 401 HTTP.
Any ideas about how to approach this?
You can simply check if any valid token is already present in the database for the same user.
token, created = Token.objects.get_or_create(user=user)
instead of above method you should use
try:
token = Token.objects.get(user=user) // Chcck if token is present
except Token.DoesNotExist:
token = Token.objects.create(user=user) // Create new token, no token for this user
else:
return Response({'error': 'Already logged in', status=400})

Cordova/Phonegap facebook plugin An active access token must be used to query information

I am using https://github.com/Wizcorp/phonegap-facebook-plugin to connect my cordova Android app to Facebook.
I can login with:
facebookConnectPlugin.login(["email"],
fbLoginSuccess,
function (error) { alert("ERROR:" + JSON.stringify(error)); }
);
, logout even call facebookConnectPlugin.getLoginStatus and get:
userID
accessToken
expiresIn
sig
status
but when FB.api('/me', function(response){...}) is called, I receive
{error:
{
message: An active access token must be used to query information about the current user,
type: OAuthException,
code: 2500
}
}
Also this only happens when the app is built, not tested in browser.
Solved issue by manually giving FB.apicall a token with:
FB.api('/me?access_token='+userdata.authResponse.accessToken, function(response) {...
where userdata is the response of facebookConnectPlugin.getLoginStatus
I just don't understand why doesn't it provide token automatically in android app like it does in browser.
Send the access token and the data retrieving fields with the apiString
const fields = ['email', 'first_name', 'last_name', 'gender', 'birthday', 'picture.type(large)'];
const apiString = `me?access_token=${accessToken}&fields=${fields.join(',')}`;

Sinatra + omniauth + Android, advice sought

I'm developing a Sinatra app for which I'd like to use OmniAuth. So far, I have something similar to this for the web app:
http://codebiff.com/omniauth-with-sinatra
I'd like the web app to be usable via Android phones which would use an API, authenticating by means of a token. The development of an API seems to be covered nicely here:
Sinatra - API - Authentication
What is not clear is now I might arrange the login procedure. Presumably it would be along these lines:
User selects what service to use, e.g. Twitter, FaceBook &c., by means of an in-app button on the Android device.
The Android app opens a webview to log in to the web app.
A token is somehow created, stored in the web app's database, and returned to the Android app so that it can be stored and used for subsequent API requests.
I'm not very clear on how point 3 might be managed - does anyone have any suggestions?
As no-one seems to have any suggestions, here's what I've come up with so far. I don't think it's very good, though.
I've added an API key to the user model, which is created when the user is first authenticated:
class User
include DataMapper::Resource
property :id, Serial, :key => true
property :uid, String
property :name, String
property :nickname, String
property :created_at, DateTime
property :api_key, String, :key => true
end
....
get '/auth/:name/callback' do
auth = request.env["omniauth.auth"]
user = User.first_or_create({ :uid => auth["uid"]},
{ :uid => auth["uid"],
:nickname => auth["info"]["nickname"],
:name => auth["info"]["name"],
:api_key => SecureRandom.hex(20),
:created_at => Time.now })
session[:user_id] = user.id
session[:api_key] = user.api_key
flash[:info] = "Welcome, #{user.name}"
redirect "/success/#{user.id}/#{user.api_key}"
end
If the authorisation works then the api_key is supplied to the Android app, which will presumably store it on the device somewhere:
get '/success/:id/:api_key', :check => :valid_key? do
user = User.get(params[:id],params[:api_key])
if user.api_key == params[:api_key]
{'api_key' => user.api_key}.to_json
else
error 401
end
end
All API calls are protected as in the link in my original post:
register do
def check (name)
condition do
error 401 unless send(name) == true
end
end
end
helpers do
def valid_key?
user = User.first(:api_key => params[:api_key])
if !user.nil?
return true
end
return false
end
end
For public use I'll only allow SSL connections to the server. Any suggestions for improvement would be welcome.

Categories

Resources