There is a bug in my code.
I am able to sign in and retrieve user information. But the signinCallback is called again(I don't know how). And it shows User information that I had earlier is gone!
Here is the HTML side:
<span id="signinButton">
<span
class="g-signin"
data-callback="signinCallback"
data-clientid="CLIENT_ID"
data-cookiepolicy="single_host_origin"
data-requestvisibleactions="http://schemas.google.com/AddActivity"
data-scope="https://www.googleapis.com/auth/plus.profile.emails.read"
data-width="standard"
data-height="short">
</span>
</span>
and here is the javascript side:
var AuthStates = {
google: null
};
function signinCallback(authResult) {
console.dir(authResult);
console.log('Sign-in state: ' + authResult['error']+authResult['access_token']);
AuthStates.google = authResult;
console.log('signinCallback');
chooseAuthProvider();
}
function chooseAuthProvider() {
if (AuthStates.google && AuthStates.facebook) {
if (AuthStates.google['access_token']) {
// Signed in with Google, you can now use Google+ APIs.
console.log(AuthStates.google);
gapi.client.load('plus','v1', function(){
var request = gapi.client.plus.people.get({
'userId': 'me'
});
request.execute(function(resp) {
document.getElementById('cname').value =resp.displayName;
document.getElementById('cemail').value =resp.emails[0].value;
console.log('Retrieved profile for:' + resp.displayName + ' ' + resp.emails[0].value);
});
});
}
}
It gives this response to the console on the second signinCallback
Sign-in state: user_signed_outundefined
signinCallback
Try updated instructions at
"Integrating Google Sign-In into your web app" page.
Your call to request.execute() in your callback method is causing the callback method to be re-triggered with "user_signed_out" value in the error property.
If you take a look at the Google documentation "Signing out the user" it reads:
When the user refreshes the page or navigates to another part of your
website, the callback will fire with user_signed_out value in the
error property until the user clicks the sign-in button again.
Hence I believe it is your call to request.execute() which is triggering the second call to the callback method.
You can guard against this second call to the callback by putting a condition within the callback method e.g.
function signinCallback(authResult) {
if (authResult['status']['signed_in']) {
console.dir(authResult);
console.log('Sign-in state: ' + authResult['error']+authResult['access_token']);
AuthStates.google = authResult;
console.log('signinCallback');
chooseAuthProvider();
}
}
See Google's documentation on "Monitoring the user's session state" for an example of the previously mentioned guard conditions.
This might be helpful for you
(function() {
var GOOGLE_PLUS_SCRIPT_URL = 'https://apis.google.com/js/client:plusone.js';
window.oauth2Callback = function(authResult) {
if (authResult['access_token']) {
accessToken = authResult['access_token'];
$(function() {
$.getScript(GOOGLE_PLUS_SCRIPT_URL);}
Related
hello i want to implement facebook account kit to login with sms on my localhost i follow the code as in the docs
<script>
// initialize Account Kit with CSRF protection
AccountKit_OnInteractive = function(){
AccountKit.init(
{
appId:"{{FACEBOOK_APP_ID}}",
state:"{{csrf}}",
version:"{{ACCOUNT_KIT_API_VERSION}}",
fbAppEventsEnabled:true,
redirect:"{{REDIRECT_URL}}"
}
);
};
// login callback
function loginCallback(response) {
if (response.status === "PARTIALLY_AUTHENTICATED") {
var code = response.code;
var csrf = response.state;
// Send code to server to exchange for access token
}
else if (response.status === "NOT_AUTHENTICATED") {
// handle authentication failure
}
else if (response.status === "BAD_PARAMS") {
// handle bad parameters
}
}
// phone form submission handler
function smsLogin() {
var countryCode = document.getElementById("country_code").value;
var phoneNumber = document.getElementById("phone_number").value;
AccountKit.login(
'PHONE',
{countryCode: countryCode, phoneNumber: phoneNumber}, // will use default values if not specified
loginCallback
);
}
// email form submission handler
function emailLogin() {
var emailAddress = document.getElementById("email").value;
AccountKit.login(
'EMAIL',
{emailAddress: emailAddress},
loginCallback
);
}
</script>
everything work as charm i enter phone and i receive sms then i verify the code and now is the problem after the code verify the login call back should be excuted so i can connect with the server but this method never execute, why?
notes: my app in development
i enabled debug true but there is no problem shown on console
i try many thing but nothing worked the login callback never run
so how i can make the login call back executed so i can connect with server?
You need to use some ajax mechanism after this line:
var csrf = response.state;
In my Symfony 3 app I made so, that if the user is inactive for some time, it is logged out and requested to login again. This is done with the following code:
//RequestListener.php
public function onKernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST != $event->getRequestType()) {
return;
}
if ($this->maxIdleTime > 0) {
$lapse = time() - $this->session->getMetadataBag()->getCreated();
$lapse_short = time() - $this->session->getMetadataBag()->getLastUsed();
if ($lapse >= $this->maxIdleTime || $lapse_short >= $this->shortIdleTime) {
$username = $this->securityToken->getToken()->getUser();
if ($username !== 'anon.'){
$this->session->invalidate();
$this->securityToken->setToken(null);
$event->setResponse(new RedirectResponse($this->router->generate('login')));
}
}
}
}
But in ths case redirect to login form is happened when the page is reloaded. I also want to force redirect on every ajax call also. By default my ajax calls are served by the following address: /ajax
But when the session is expired the ajax is 'redirected' to my login page address and in browsers Network tab I see the following:
My ajax function which is supposed to redirect is as follows:
function requestAjax(json_data, url) {
if(url.indexOf('login') !== -1){
window.location = './login';
}
var request = $.ajax({
url: root + '/' + url
, method: 'post'
, data: json_data
});
return request;
}
But no redirect is happened. So The question is how to force redirect on expired sessions and ajax calls and also why ajax status is 200 but not say 302 in this case? Thank you
UPD_1 My services.yml for RequestListener.php
app.handler.session_idle:
class: AppBundle\EventListener\RequestListener
arguments: ["#session", "#security.token_storage", "#router", "#app.logger", "%session_lifetime%", "%session_active_lifetime%", "%instance_path%"]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
You could try something like this (tested and working in Symfony 2.8)
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
class AjaxAuthenticationListener {
/*
* function onCoreException
* Check if session is expired and handles security related exceptions
* #param GetResponseForExceptionEvent $event An GetResponseForExceptionEvent instance
*
*/
public function onCoreException(GetResponseForExceptionEvent $event) {
$exception = $event->getException();
$event_request = $event->getRequest();
$session = $event->getRequest()->getSession();
if ($event_request->isXmlHttpRequest()) {
if ($exception instanceof AuthenticationException || $exception instanceof AccessDeniedException) {
$session->getFlashBag()->add('warning', 'You have been signed out automatically due to inactivity.');
$event->setResponse(new Response('Session expired', 403));
}
}
}
}
As you can see, "onCoreException" function returns a 403 status code.
Now, in home page (in my case) or page where you will have ajax calls, you could use "ajaxError" and catch the jqXHR.status, if it is 403, then redirect to login page and using a "FlashBag" to display a message related to expired session.
$(document).ready(function () {
//Catch AjaxAuthenticationListener response
$(document).ajaxError(function (event, jqXHR) {
if (403 === jqXHR.status) {
$(location).attr('href', '{{ path('login') }}');
}
});
I have omitted explain how "onCoreException" function works as a service and how it handles the session when it has been expired, taking into account that this part is working properly in your code.
services.yml:
app.gt.ajax.authentication.listener:
class: AppBundle\EventListener\AjaxAuthenticationListener
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onCoreException, priority: 1000 }
I hope this is useful for you.
Symfony 5 solution
Have been researching on this care for quite some hours. In the symfony 5 How to Customize Access Denied Responses docs, you can customize one of the following:
1. App entry point
2. Access denied handler
3. All Access Denied Responses
Going with customizing All Access Denied Responses, i created a kernel.exception subscriber/listener:
namespace App\EventSubscribers;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class AccessDeniedHandler implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
// the priority must be greater than the Security HTTP
// ExceptionListener, to make sure it's called before
// the default exception listener
KernelEvents::EXCEPTION => ['onKernelException', 2]
];
}
public function onKernelException(ExceptionEvent $event): void
{
// Ajax is returning login page instead of session expired/access denied
// Creating a custom handler for ajax
// more at https://symfony.com/doc/current/security/access_denied_handler.html#customize-the-unauthorized-response
$request = $event->getRequest();
if($request->isXmlHttpRequest()){
$event->setResponse(new Response('Your session has expired!', 403));
return;
}
}
}
I am implementing Google log in for the first time as described here and here.
I am using HTML with Javascript.
The problem that needs solving is as follows: How can I, after the initial login, on a different page (say a landing page, or portal that the user sees after logging in), check if the user is logged in? Is there a service I can call to check the user's login in status with my app key or something similar?
I assume I would have to include the google API on each page.
Login Page Code:
Script In Head (Code from Google's tutorial listed above):
<head>
....
<script src="https://apis.google.com/js/platform.js" async defer></script>
<script>
function onSignIn(googleUser)
{
var profile = googleUser.getBasicProfile();
console.log('ID: ' + profile.getId());
console.log('Name: ' + profile.getName());
console.log('Image URL: ' + profile.getImageUrl());
console.log('Email: ' + profile.getEmail());
alert(profile.getName());
}
function logout()
{
alert('logging out');
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('User signed out.');
});
}
...
</head>
Code In Body (1st line from Google's tutorial listed above, 2nd line to trigger logout test)
<body>
...
<div class="g-signin2" data-onsuccess="onSignIn"></div>
<div onmousedown="logout()">Logout</div>
...
</body>
Is there some way I can include the google API on another page, and then call some check login status function? Or another way to concretely tell if the user is logged in or out?
You do not need to store anything on local storage. The library allows you to check if the user is logged in or not using the isSignedIn.get() on the auth2 of the gapi object.
Load the JavaScript library, make sure you are not deferring the load :
<script src="https://apis.google.com/js/platform.js"></script>
Then initialize the library and check if the user is logged in or not
var auth2;
var googleUser; // The current user
gapi.load('auth2', function(){
auth2 = gapi.auth2.init({
client_id: 'your-app-id.apps.googleusercontent.com'
});
auth2.attachClickHandler('signin-button', {}, onSuccess, onFailure);
auth2.isSignedIn.listen(signinChanged);
auth2.currentUser.listen(userChanged); // This is what you use to listen for user changes
});
var signinChanged = function (val) {
console.log('Signin state changed to ', val);
};
var onSuccess = function(user) {
console.log('Signed in as ' + user.getBasicProfile().getName());
// Redirect somewhere
};
var onFailure = function(error) {
console.log(error);
};
function signOut() {
auth2.signOut().then(function () {
console.log('User signed out.');
});
}
var userChanged = function (user) {
if(user.getId()){
// Do something here
}
};
Don't forget to change the app id
You can stringify a custom userEntity object and store it in sessionStorage where you can check it anytime you load a new page. I have not tested the following but it should work (doing something similar with WebAPI tokens in the same way)
function onSignIn(googleUser)
{
var profile = googleUser.getBasicProfile();
console.log('ID: ' + profile.getId());
console.log('Name: ' + profile.getName());
console.log('Image URL: ' + profile.getImageUrl());
console.log('Email: ' + profile.getEmail());
var myUserEntity = {};
myUserEntity.Id = profile.getId();
myUserEntity.Name = profile.getName();
//Store the entity object in sessionStorage where it will be accessible from all pages of your site.
sessionStorage.setItem('myUserEntity',JSON.stringify(myUserEntity));
alert(profile.getName());
}
function checkIfLoggedIn()
{
if(sessionStorage.getItem('myUserEntity') == null){
//Redirect to login page, no user entity available in sessionStorage
window.location.href='Login.html';
} else {
//User already logged in
var userEntity = {};
userEntity = JSON.parse(sessionStorage.getItem('myUserEntity'));
...
DoWhatever();
}
}
function logout()
{
//Don't forget to clear sessionStorage when user logs out
sessionStorage.clear();
}
Of course, you can have some internal checks if the sessionStorage object is tampered with. This approach should work with modern browsers like Chrome and Firefox.
To check is user Signed-in use:
gapi.auth2.getAuthInstance().isSignedIn.get()
Adding on to Joseph's answer above, you can then get information about the user by calling auth2.currentUser.get().getBasicProfile().
if (auth2.isSignedIn.get()) {
googleUserProfile = auth2.currentUser.get().getBasicProfile()
console.log('ID: ' + googleUserProfile.getId());
console.log('Full Name: ' + googleUserProfile.getName());
console.log('Given Name: ' + googleUserProfile.getGivenName());
console.log('Family Name: ' + googleUserProfile.getFamilyName());
console.log('Image URL: ' + googleUserProfile.getImageUrl());
console.log('Email: ' + googleUserProfile.getEmail());
}
From the docs: https://developers.google.com/identity/sign-in/web/people
according to the link from Phyrik post (https://developers.google.com/identity/sign-in/web/people) -
google stopped supporting the Sign-In Javascript web auth API:
We are discontinuing the Google Sign-In JavaScript Platform Library for web. The library will be unavailable for download after the March 31, 2023 deprecation date. Instead, use the new Google Identity Services for Web.
By default, newly created Client IDs are now blocked from using the older Platform Library, existing Client IDs are unaffected. New Client IDs created before July 29th, 2022 can set plugin_name to enable use of the Google Platform Library.
I have the following scenario. Actual Page loading starts, user login is checked for authentication. If access granted, actual page loading completes and user can access the page. If access denied, actual page loading stops and user is redirected to 'access denied' page.
Infact the scenario should be like this. User authentication is checked. if access granted, actual page loading starts and user can access page. If access denied, user is directly directed to 'access denied' page.
can someone tell me how to include promise for this scenario. current code is as follows.
$q.when().then(function () {
return $rootScope.$emit('resetView', false, 'default');
}).then(function (result) {
loadNavBar(); //actual page loading starts here
}, function (error) {
$log.error("Caught an error:", error);
return $q.reject('New error');
});
the below function is loadNavBar() which gets executed. User authentication is done inside of this. Hence page loading starts and then user is checked. I want user to be checked first itself and then load page accordingly depending on his access rights.
var loadNavBar = function () {
//few functions here to display page.
//below code to check user authentication
var serviceURL_CheckUserExists = '/api/Pre/CheckUserExists';
//ajax to check if user exists in database. give/ deny access based on user present in DB and if user is set as blockuser in db.
$.ajax({
type: "GET",
url: serviceURL_CheckUserExists,
}).then(function (response) {
if (response.Results.length == 1 && response.Results[0].BlockUser == false) { //user has access if condition is satisfied.
$rootScope.myLayout.eventHub.emit('getUserName', response.Results[0].User_ID.trim());
$scope.role = "";
var details = response.Results[0];
for (var parameters in details) {
if (details[parameters] == true) {
$scope.role += parameters + ',';
}
}
$scope.role = $scope.role.replace(/.$/, ".");
var firstname = response.Results[0].FirstName;
firstname = firstname.replace(/\s/g, '');
$scope.$apply(function () {
$scope.username = response.Results[0].FirstName + " " + response.Results[0].LastName;
});
}
else { $window.location.href = '../../../BlockUser.html'; } //block access to actual page and redirect to 'access denied' page.
}
}
});
};
i think that the right approach to your problem is to use resolve property in the route, so the user can't navigate to certain pages if he isn't logged in and once he logged in you can inject the user object to the controller
for example to navigate to home page you must be logged in
.when("/home", {
templateUrl: "homeView.html",
controller: "homeController",
resolve: {
user: function(AuthenticationService){
return AuthenticationService.getUser();
}
}
})
app.controller("homeController", function ($scope, user) {
$scope.user = user;
});
https://www.sitepoint.com/implementing-authentication-angular-applications/
Here's a quick example of hiding the content until the user is authenticated to see it. Click the 'authenticate' button to trigger the function that you would run if the user is authenticated by your ajax call. Showing the content can be done with a fuction like:
function userIsAuthenticated(){
document.getElementById('pageContent').style.display = 'block';
}
See JsFiddle for a simple implementation.
On my forum-based website, I have a link below every post for reporting spam or abuse. Whenever this link is clicked, a web service is called on the server, when the call returns, the span containing the link (see the code below) is updated with something like 'Post reported' or if an error occurs it shows something like 'An error occurred while reporting the post', this is the javascript code:
<script src="/js/MicrosoftAjax.js" type="text/javascript" language="javascript"></script>
<script type="text/javascript" language="javascript">
var spanToUpdate;
function ReportPost(updateSpan, postID)
{
if (confirm("Are you sure you want to report this post as spam or abuse?"))
{
spanToUpdate = updateSpan;
var proxy = SiteWS.ReportPost(postID, onReportPostSuccess, onReportPostFailure);
}
}
function onReportPostSuccess(sender, e)
{
spanToUpdate.innerHTML = "Post reported";
}
function onReportPostFailure(sender, e)
{
spanToUpdate.innerHTML = "An error occurred while reporting the post";
}
</script>
And this is the reporting link:
<div class="post">
<p>post text here</p>
<span>Report Post</span>
</div>
Other posts ...
As you can see, I use a variable, spanToUpdate, to hold a reference to the span that contains the reporting link, which means that if the user reports another post (ie. clicks another reporting link) before the call returns, the span of the last post will be updated twice and the previous one won't be updated at all, is there any workaround for this?
Many thanks
You can use anonymous functions and closures for that.function ReportPost(updateSpan, postID) {
if (confirm("Are you sure you want to report this post as spam or abuse?")) {
var proxy = SiteWS.ReportPost(
postID,
function(sender,e) {updateSpan.innerHTML = "Post reported" },
function(sender,e) {updateSpan.innerHTML = "An error occurred while reporting the post" }
);
}
}
edit: hmm .. just wondering, will updateSpan be referring to the same span when the anonymous method is called? – Waleed Eissa
Yes, that's the magic of closures. Try this little example:
<head>
<script>
function foo()
{
bar(1, 100);
bar(2, 150);
bar(3, 200);
bar(4, 250);
bar(5, 300);
document.getElementById("div1").innerHTML += "foo() is done. ";
return;
}
function bar(val, timeout) {
window.setTimeout(
function() {
document.getElementById("div1").innerHTML += " " + val + " ";
},
timeout
);
}
</script>
</head>
<body>
<button onclick="foo()">click</button>
<div id="div1"></div>
</body>You will see that each time the anonymous function is called it has preserved "its own" value of val from the time/context when bar() was called.
Not a JavaScript developer so this might not work. Would it be possible to hold a reference to the post id and the spanToUpdate and then have the response from the server include the post id. Then you could retrieve the correct spanToUpdate.