I have a really weird scenario that I'm stuck on. I have a ASP.Net MVC 4 app where I'm authenticating a user and creating an authCookie and adding it to the response's cookies then redirecting them to the target page:
if (ModelState.IsValid)
{
var userAuthenticated = UserInfo.AuthenticateUser(model.UserName, model.Password);
if (userAuthenticated)
{
var userInfo = UserInfo.FindByUserName(model.UserName);
//SERIALIZE AUTHENTICATED USER
var serializer = new JavaScriptSerializer();
var serializedUser = serializer.Serialize(userInfo);
var ticket = new FormsAuthenticationTicket(1, model.UserName, DateTime.Now, DateTime.Now.AddMinutes(30), false, serializedUser);
var hash = FormsAuthentication.Encrypt(ticket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash) {Expires = ticket.Expiration};
Response.Cookies.Add(authCookie);
if (Url.IsLocalUrl(model.ReturnUrl) && model.ReturnUrl.Length > 1 && model.ReturnUrl.StartsWith("/") && !model.ReturnUrl.StartsWith("//") && !model.ReturnUrl.StartsWith("/\\"))
{
return Redirect(model.ReturnUrl);
}
var url = Url.Action("Index", "Course");
return Redirect(url);
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
This is working just fine in all browsers. I can login and access the secure pages in my app.
My client is requesting an android version of this app. So, I'm trying to figure out how to convert this app into an APK file. My first attempt is to create a simple index.html page with an iframe that targets the application. This works just fine in Firefox and IE 9. However, when accessing the index.html page that contains the iframe that points to the app via Chrome, I get past the login code above and the user gets redirected to the secure controller, but the secure controller has a custom attribute to make sure the user is authenticated:
public class RequiresAuthenticationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated) return;
if (filterContext.HttpContext.Request.Url == null) return;
var returnUrl = filterContext.HttpContext.Request.Url.AbsolutePath;
if (!filterContext.HttpContext.Request.Browser.IsMobileDevice)
{
filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + string.Format("?ReturnUrl={0}", returnUrl), true);
}
else
{
filterContext.HttpContext.Response.Redirect("/Home/Home", true);
}
}
}
My app is failing on: filterContext.HttpContext.User.Identity.IsAuthenticated. IsAuthenticated is always false, even though the user was authenticated in the code above.
Keep in mind this only happens when accessing the app via iframe in Chrome. If I access the app directly instead of via iframe, then everything works just fine.
Any ideas?
UPDATE:
My controller extends SecureController. In the constructor of SecureController I have the code that deserializes the user:
public SecureController()
{
var context = new HttpContextWrapper(System.Web.HttpContext.Current);
if (context.Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
var serializer = new JavaScriptSerializer();
var cookie = context.Request.Cookies[FormsAuthentication.FormsCookieName].Value;
var ticket = FormsAuthentication.Decrypt(cookie);
CurrentUser = serializer.Deserialize<UserInfo>(ticket.UserData);
}
else
{
CurrentUser = new UserInfo();
}
//if ajax request and session has expired, then force re-login
if (context.Request.IsAjaxRequest() && context.Request.IsAuthenticated == false)
{
context.Response.Clear();
context.Response.StatusCode = 401;
context.Response.Flush();
}
}
First, you should be deriving from AuthorizeAttribute, not an ActionFilterAttribute. Authorization attributes execute before the method is even called at a higher level of the pipeline, while ActionFilters execute much further down, and other attributes can execute before yours.
Secondly, you aren't showing the code you use to decrypt the ticket and set the IPrincipal and IIdentity. Since that's where the problem is, it's odd that you didn't include it.
Related
I'm facing an issue,
I've created some code to be able to login in my app with facebook.
The steps are working as expected:
The external facebook login web page opens
The Second activity intercept the redirectUrl after a successfull login
At this point OAuth2Autenticator.Completed(object o, AuthenticatorCompletedEventArgs e) is called but e is not authenticated.
My code in ViewModel:
async Task FacebookAuth()
{
var facebookAppId = "0123...";
_authenticator = new OAuth2Authenticator(
clientId: facebookAppId,
scope: "email",
authorizeUrl: new Uri("https://www.facebook.com/dialog/oauth/"),
redirectUrl: new Uri("https://www.myRedirectUrl.com"),
isUsingNativeUI: true
);
_authenticator.Completed += Authenticator_Facebook_Completed;
_authenticator.Error += _authenticator_Error;
var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
presenter.Login(_authenticator);
}
In the interceptor activity:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
var mUri = new Uri(Intent.Data.ToString());
_authenticator.OnPageLoading(mUri);
Finish();
}
Back to the viewModel:
private async void Authenticator_Facebook_Completed(object sender, AuthenticatorCompletedEventArgs e)
{
_authenticator.Completed -= Authenticator_Facebook_Completed;
if (e.IsAuthenticated)
{
var acc = e.Account;
var val = JsonConvert.SerializeObject(e.Account);
var model = JsonConvert.DeserializeObject<FacebookSigninModel>(val);
var user = await GetFacebookProfile(model.Properties.AccessToken);
await SignInToAPI(new APIUser
{
TokenId = model.Properties.AccessToken,
Email = user.Email,
FamilyName = user.FirstName,
GivenName = user.LastName,
Picture = user.ProfilePic
}, AuthProvider.Facebook);
}
}
Here e is not authenticated and i have a null account.
Another strange detail is that even if i remove the line:
_authenticator.OnPageLoading(mUri);
the method Authenticator_Facebook_Completed is called anyway with the exact same result.
I've also tried to change the redirect url in order to no catch it in the interceptor activity.
So as expected i have to manually go back to my app.
Whe the system returns to my app, the Authenticator_Facebook_Completed method is called with parameter values equals to the other approaches.
I have written my code following this sample:
https://github.com/coolc0ders/SocialAuthXamarinFormsAspNetCore/tree/master/AuthDemoXForms
Running this app the Authenticator_Facebook_Completed is called only after the _authenticator.OnPageLoading(Uri); is called too and not when the system reopens the app after the redirect from the web page.
I cannot figure out why my Authenticator_Facebook_Completed is always called no matter what and why _authenticator.OnPageLoading(mUri); has no relevance at all in my code.
Thanks,
I'm trying to run my desktop chrome extension on Android, so i tried running it in Yandex browser on my phone. It runs ok except for the google login.(everything works well on desktop Chrome and desktop Yandex).
This code is called by the background scripts:
var url = 'https://accounts.google.com/o/oauth2/auth' +
'?client_id=' + clientId +
'&response_type=id_token' +
'&access_type=offline' +
'&redirect_uri=' + redirectUri +
'&scope=' + scopes;
getIdToken: function (message) {
const _this = this;
var idToken = "";
chrome.identity.launchWebAuthFlow(
{
'url': url,
'interactive': true
},
function (redirectedTo) {
console.log("[2]auth-manager called: "+redirectedTo);
if (chrome.runtime.lastError) {
// Example: Authorization page could not be loaded.
console.log("lastError: "+chrome.runtime.lastError.message);
}
else {
var response = redirectedTo.split('#', 2)[1];
// Example: id_token=<YOUR_BELOVED_ID_TOKEN>&authuser=0&hd=<SOME.DOMAIN.PL>&session_state=<SESSION_SATE>&prompt=<PROMPT>
idToken = (response.split('&')[0]).split('=', 2)[1];
console.log("auth-manager id token", idToken);
if (message != undefined) {
message.data.google_id_token = idToken;
cloudWebSocket.sendMessage(JSON.stringify(message));
_this.isLogged = true;
closePopup();
alert('login successful');
}
}
}
);
}
When I call this function, redirectedTo is undefined, and i get a chrome.runtime.lastError.message:"canceled". That's it.
I use the very same manifest from the desktop apps, with the same clientId,redirectUri and scopes.
I can't figure out, what causes this problem?
If there's another way to perform a google login without this issue it can also help.
see the Web Extensions API ...in particular the platform support table there.
initiating the oAuth2 flow on the server-side should nevertheless be possible.
I have an ASP.NET C# MVC4 Web site that I have working wonderfully for the most part. However, when we tested on mobile, the cookies that I am using for authentication would not work. I set the Auth cookie in my controller action but when trying to access them on the next call they are not there. Once again this is ONLY A PROBLEM ON MOBILE. Works fine in desktop versions of IE, Chrome and Firefox. Does not work with Chrome on Android.
Code to write cookie (in controller action):
//Set information into object that can be read out of the cookie later
FormsAuthModel UserDataObj = new FormsAuthModel
{
UserID = dmUser.ID,
PasswordChange = dmUser.PasswordChange
};
string UserData = Convert.ToBase64String(clsShared.Serialize(UserDataObj));
//Create the ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, dmUser.UserName, DateTime.Now, DateTime.Now.AddDays(1), false, UserData, FormsAuthentication.FormsCookiePath);
// Encrypt the ticket
string encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
System.Web.HttpContext.Current.Response.Cookies.Add(cookie);
Code to read cookie (in Global.asax.cs - Application_PostAuthenticateRequest):
HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
try
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
UserDataObj = (FormsAuthModel)clsShared.Deserialize(Convert.FromBase64String(authTicket.UserData), typeof(FormsAuthModel));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
//WriteEvent(string.Format("Error deserializing auth ticket - {0}", ex.Message), EventLogEntryType.Error);
}
}
The AuthCookie is always null on the subsequent requests. What the user sees is a login screen, they fill it out and they get redirected right back to the login screen.
I could not find anything in my searches that helped explain why all the mobile requests (my phone, my tablet and other users' phones) would act differently than the desktop browsers.
Any help would be greatly appreciated.
Thanks!!
OK I found a solution although I am not sure why. I changed the cookie creation code as follows and it worked.
//Set information into object that can be read out of the cookie later
FormsAuthModel UserDataObj = new FormsAuthModel
{
UserID = dmUser.ID,
PasswordChange = dmUser.PasswordChange
};
string UserData = Convert.ToBase64String(clsShared.Serialize(UserDataObj));
//Create the ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, dmUser.UserName, DateTime.Now, DateTime.Now.AddDays(1), false, UserData, FormsAuthentication.FormsCookiePath);
// Encrypt the ticket
string encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie - FIX IS HERE!!!
Response.Cookies[FormsAuthentication.FormsCookieName].Value = encTicket;
//HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
//Response.Cookies.Add(cookie);
Notice that the only change is in adding the cookie by setting the value directly instead of creating a cookie object and adding that to the collection.
i.e. - Response.Cookies["Name"] = Value;
I got the idea from this MS article: https://msdn.microsoft.com/en-us/library/ms178194.aspx.
So does anyone know why this would make a difference? I have used the cookie instance method several times before and never had this problem.
I need to authenticate via Android on my website (Zend Framework2+ZfcUser+ZfcUserDoctrineORM).
I want to call an url that authenticate me and return a json object that contains my session_id.
I don't know if it is the correct way but whatever i don't know how to do that with zfcUser.
David
Next, i will be able to store this session_id into Shared Preferences storage.
First of all sorry for my English.
In my application i need almost the same.
Ok. So in yoursite.com/module/YourModuleName/Module.php do:
use YourModuleName\Model\YourModuleName;
use YourModuleName\Model\YourModuleName;
class Module {
public function onBootstrap(MvcEvent $e) {
$app = $e->getApplication();
$em = $app->getEventManager();
$sm = $app->getServiceManager();
$auth = $sm->get('zfcuser_auth_service');
$model = new OrdersManager();
if (!$auth->hasIdentity()) {
$em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($app, $sm, $auth, $model) {
$match = $e->getRouteMatch();
// No route, this is a 404
if (!$match instanceof RouteMatch) {
return;
}
$match = $e->getRouteMatch();
$matchParams = $match->getParams();
// $matchParams['hash'] == some url param
if (isset($matchParams['hash'])) {
$model->setDbAdapterColibo($sm->get('dbAdapter'));
$usersSqlObject = $model->getUsers();
$salt = md5('caw');
foreach ($usersSqlObject as $key => $user) {
$hash = hash('sha256', $salt.$param1.$user['user_id']);
if ($hash == $matchParams['hash']) {
$authAdapter = $sm->get('ZfcUser\Authentication\Adapter\AdapterChain');
$request = $app->getRequest();
$request->getPost()->set('identity', $user['email']);
// You may use user password to auth user
$request->getPost()->set('credential', $user['user_id']);
$result = $authAdapter->prepareForAuthentication($request);
$auth->authenticate($authAdapter);
// do your staff with session or other.
// after this you will be redirect to page from where query was
break;
}
}
}
});
}
}
}
Don`t forget about yoursite.com/module/YourModuleName/config/module.config.php
You need to add route with your URL param, to receive it in $matchParams = $match->getParams();
In case I have describe you will be auth and immediately redirect to the site.
Example:
http://example.com/someController/someAction/param1/param2/hash...
the result will be auth and open page http://example.com/someController/someAction/param1/param2/hash...
Ok. This is what i need for my app. Hope this help.
P.S. Some ideas get from Zend Framework 2 - Global check for authentication with ZFCUser
I am creating an (iphone/android) mobile app using appcelerator titanium. I have a problem using Ti.App.Properties,
I want to save the user's login data (username and password), I used Ti.App.Properties's getList and setList methods to get and set username and password at app startup. It is working fine on iPhone, but on android the data (username and password) are not retrieved at app startup.
here is the code that is executed at app startup :
var userDataArray=[{title:'name',value:''},
{title:'password',value:''}];
if(Ti.App.Properties.hasProperty("userDataArray"))
{
userDataArray = Ti.App.Properties.getList("userDataArray");
}
else
{
Ti.App.Properties.setList("userDataArray",userDataArray);
}
if((Ti.App.Properties.getList("userDataArray")[0].value.length==0)||(Ti.App.Properties.getList("userDataArray")[1].value.length==0))//check if name, password have no values.. on android, this is always the case, which is not correct
{
//go to login page
}
else if((Ti.App.Properties.getList("userDataArray")[0].value.length>0)&&(Ti.App.Properties.getList("userDataArray")[1].value.length>0))//if both username and password exist
{
//start
}
Thank you
i think your overall approach is flawed, you dont need an array just an map
// save the values as a string..
Ti.App.Properties.setString({"username":"myname", "password":"mypassword"}, "CREDENTIALS");
// retrieve the values as a string, but parse it back into an object
var credObject = JSON.parse(Ti.App.Properties.getString("CREDENTIALS"));
// dump the output
Ti.API.debug("Username "+ credObject.username);
Ti.API.debug("Password "+ credObject.password);
two remarks :
arguments for .setString() is opposite, ie Name then Value
Value must be a string, so you have to stringify() it or enter it as a string
I know this is old, but it's still relevant today as there's not a huge amount of help with Titanium. I handle this in two parts.
Part 1) After the user's credentials have been authenticated...
var username = "some username";
var password = "some password";
// Build the object and then convert it to a json string.
oCredentials = new Object();
oCredentials.username = username;
oCredentials.password = password;
var stringCredentials = JSON.stringify(oCredentials);
// Save the credentials
Ti.App.Properties.setString("Credentials", stringCredentials);
Part 2) Before you prompt the user with the login window/popup/whatever...
// Look for credentials
(function() {
var storedCredentials = Ti.App.Properties.getString("Credentials");
if (storedCredentials){
var oJson = JSON.parse(storedCredentials);
// Call your authentication function
// For example, autoAuthenticate(oJson.username, oJson.password);
} else {
// kick the user out to your login window
// For example, $.loginWindow.open();
}
})();