MS Auth Result Not Responding - authentication

I'm exploring the Microsoft Graph UWP Tutorial [1]: https://learn.microsoft.com/en-us/graph/tutorials/uwp?tutorial-step=1 and having difficulty with the app.
An inconsistent behavior occurs with the "Sign-In". Sometimes after the username and password are entered the information is "accepted" and a token is provided. Control then opens the HomePage. However, after signing out and re-entering the login details, the app just hangs indefinitely and a little blue flashing dot appears in the upper left hand corner.
I have tried multiple live.com user accounts and the behavior is the same. Since I use the MSAL for my other apps, I'm seeing the same result. I'm using VS 2022 .
Here is the affected code:
public MainPage()
{
this.InitializeComponent();
// Load OAuth settings
var oauthSettings = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView("OAuth");
var appId = oauthSettings.GetString("AppId");
var scopes = oauthSettings.GetString("Scopes");
if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(scopes))
{
Notification.Show("Could not load OAuth Settings from resource file.");
}
else
{
// Configure MSAL provider
MsalProvider.ClientId = appId;
MsalProvider.Scopes = new ScopeSet(scopes.Split(' '));
// Handle auth state change
ProviderManager.Instance.ProviderUpdated += ProviderUpdated;
// Navigate to HomePage.xaml
RootFrame.Navigate(typeof(HomePage));
}
}
// </ConstructorSnippet>
// <ProviderUpdatedSnippet>
private void ProviderUpdated(object sender, ProviderUpdatedEventArgs e)
{
var globalProvider = ProviderManager.Instance.GlobalProvider;
SetAuthState(globalProvider != null && globalProvider.State == ProviderState.SignedIn);
RootFrame.Navigate(typeof(HomePage));
}
// </ProviderUpdatedSnippet>
// <SetAuthStateSnippet>
private void SetAuthState(bool isAuthenticated)
{
(Application.Current as App).IsAuthenticated = isAuthenticated;
// Toggle controls that require auth
Calendar.IsEnabled = isAuthenticated;
NewEvent.IsEnabled = isAuthenticated;
}
// </SetAuthS
It is very inconsistent..sometimes the login/password is accepted and the program continues, however, most of the times, it just hangs.
I've checked for some type of "time-out" setting where multiple logins with the same time period will not be accepted, but could find no solution.
And yes, I've checked with MS Forums, but that has been a bit of a black hole.

Related

How to make friends Facebook Log in code with Google log in code

I have two snippets of code that are each responsible for logging in from their social networks are Facebook and Google.
//GOOGLE
if(isset($_GET['code'])) {
$token = $google_client->fetchAccessTokenWithAuthCode($_GET["code"]);
//This condition will check there is any error occur during geting authentication token. If there is no any error occur then it will execute if block of code/
if (!isset($token['error'])) {
//Set the access token used for requests
$google_client->setAccessToken($token['access_token']);
//Store "access_token" value in $_SESSION variable for future use.
$_SESSION['access_token'] = $token['access_token'];
//Create Object of Google Service OAuth 2 class
$google_service = new Google_Service_Oauth2($google_client);
//Get user profile data from google
$data = $google_service->userinfo->get();
if (!empty($data['sub'])) {
$_SESSION['user_id'] = $data['sub'];
}
if (!empty($data['given_name'])) {
$_SESSION['user_name'] = $data['given_name'] . " " . $data['family_name'];
}
if (!empty($data['email'])) {
$_SESSION['user_email_address'] = $data['email'];
}
if (!empty($data['picture'])) {
$_SESSION['user_image'] = $data['picture'];
}
}
}
//FACEBOOK
$facebook_helper = $facebook->getRedirectLoginHelper();
if(isset($_GET['code'])) {
if (isset($_SESSION['access_token'])) {
$access_token = $_SESSION['access_token'];
} else {
$access_token = $facebook_helper->getAccessToken();
$_SESSION['access_token'] = $access_token;
$facebook->setDefaultAccessToken($_SESSION['access_token']);
}
$graph_response = $facebook->get("/me?fields=name,email", $access_token);
$facebook_user_info = $graph_response->getGraphUser();
if (!empty($facebook_user_info['id'])) {
$_SESSION['user_image'] = 'http://graph.facebook.com/' . $facebook_user_info['id'] . '/picture';
}
if (!empty($facebook_user_info['id'])) {
$_SESSION['user_id'] = $facebook_user_info['id'];
}
if (!empty($facebook_user_info['name'])) {
$_SESSION['user_name'] = $facebook_user_info['name'];
}
if (!empty($facebook_user_info['email'])) {
$_SESSION['user_email_address'] = $facebook_user_info['email'];
}
} else {
// Get login url
$facebook_permissions = ['email']; // Optional permissions
$facebook_login_url = $facebook_helper->getLoginUrl('https://2goe.com/demo/'.$lang.'/home/', $facebook_permissions);
}
When they are together, then:
When you click Google log in, redirectURL responds with server error 500.
And Facebook does not return user data, which is requested in the code.
But if, for example, you delete the code of one of the social networks, it individually works fine. I myself tried to somehow paint 2 codes into one code, but to no avail. I also split them into different files, but this also did not bring any results.
Can you please help me somehow combine them correctly so that there are no such conflicts between them.
The issue you are having is that you are setting things with the same session names. $_GET['code'] could be facebook or google there is no way for you to know which one it is.
The easiest solution would be to run the google code first. Then alter the if statement for Facebook a little.
If you do something like this the code for facebook will look for a code find a code but it will also look for an error from Google. If google spit back an error then you try the code with facebook. If it did not return an error then you know the code was most likely used by Google.
if(isset($_GET['code'] && isset($token['error'])) {

Read the SMS activation code automatically in Xamarin Forms instead of manually typing it by user

I wrote a project with Xamarin Forms. When every user has signed up, I send him/her an activation Code to confirm it and the user has to insert it to enter the app. But I am looking for a plugin or a way that the user does not need to insert the activation Code.
I want the activation Code to be read automatically without the need to enter it manually.
First add the required permissions in AndroidManifest:
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
Here is SmsReceiver class in Android project:
using System.Linq;
using System.Text.RegularExpressions;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Telephony;
using Java.Lang;
using Xamarin.Forms;
namespace MyProject.Android
{
[BroadcastReceiver(Enabled = true, Label = "SMS Receiver")]
[IntentFilter(new string[] { "android.provider.Telephony.SMS_RECEIVED", Intent.CategoryDefault })]
public class SmsReceiver : BroadcastReceiver
{
private const string IntentAction = "android.provider.Telephony.SMS_RECEIVED";
private static readonly string Sender = "SMS Sender number here";
private static readonly string[] OtpMessageBodyKeywordSet = {"Keyword1", "Keyword2"}; //You must define your own Keywords
public override void OnReceive(Context context, Intent intent)
{
try
{
if (intent.Action != IntentAction) return;
var bundle = intent.Extras;
if (bundle == null) return;
var pdus = bundle.Get("pdus");
// var castedPdus = JNIEnv.GetArray(pdus.Handle);
var castedPdus = JNIEnv.GetArray<Object>(pdus.Handle);
var msgs = new SmsMessage[castedPdus.Length];
var sb = new StringBuilder();
string sender = null;
for (var i = 0; i < msgs.Length; i++)
{
var bytes = new byte[JNIEnv.GetArrayLength(castedPdus[i].Handle)];
JNIEnv.CopyArray(castedPdus[i].Handle, bytes);
string format = bundle.GetString("format");
msgs[i] = SmsMessage.CreateFromPdu(bytes, format);
if (sender == null)
sender = msgs[i].OriginatingAddress;
sb.Append(string.Format("SMS From: {0}{1}Body: {2}{1}", msgs[i].OriginatingAddress,
System.Environment.NewLine, msgs[i].MessageBody));
//Toast.MakeText(context, sb.ToString(), ToastLength.Long).Show();
//Log.Error("Vahid", sb.ToString());
var msgBody = msgs[i].MessageBody;
if(!sender.Contains(Sender)) return;
bool foundKeyword = OtpMessageBodyKeywordSet.Any(k => msgBody.Contains(k));
if (!foundKeyword) return;
var code = ExtractNumber(msgBody);
MessagingCenter.Send<RegisterSecondPageModel, string>(new RegisterSecondPageModel(), "OtpReceived", code);
}
}
catch (System.Exception ex)
{
//Toast.MakeText(context, ex.Message, ToastLength.Long).Show();
}
}
private static string ExtractNumber(string text)
{
if (string.IsNullOrEmpty(text)) return "";
var regPattern = #"\d+";
var number = Regex.Match(text, regPattern).Value;
return number;
}
}
}
Note: In order to filter out the coming SMSes and detect only our own SMS we can apply these two filters:
1- Ignoring all SMSes that their sender numbers are not our SMS sender number.
2- Sometimes our SMS sender might send different SMSes to our clients, for example one SMS to send an activation code, and another to inform and confirm user's successfully registration in system. That said, we gotta distinguish them. For that we can search message body in order to find some pre-defined keywords. Of course our SMS server has to stick to a defined body format. "Activation", "Code", "Activation code" could be some example keywords in English language. Of course keywords should be defined in each language accordingly.
Here is RegisterSecondPageModel inside PCL project:
public class RegisterSecondPageModel
{
public RegisterSecondPageModel()
{
SubscribeToOtpReceiving();
}
private void SubscribeToOtpReceiving()
{
MessagingCenter.Subscribe<RegisterSecondPageModel, string>(this, "OtpReceived", (sender, code) =>
{
ActivationCode = code;
});
}
}
Another note is that as Jason already said, iOS doesn't allow apps to read SMSes.
If you're already sure about your clients having a SIM card in their device, then you can create a token and authenticate backwards, sending an SMS containing your token to from clients' device to your number.
Pros:
No blocked numbers: Sending messages from client is not blocked even if you're on their blacklist or they're blocking advertisements and unknown senders.
No costs on your side for authentication.
This works also in iOS which you can't read but can send messages.
Cons:
Client may be using another number in another device. This can be overcome by creating easy-to-type tokens which expire fast enough not to attract brute force attacks.
Client may not be able to send an SMS to your number due to several reasons including but not limited to not having enough account charge.

ETrade API unattended authentication

Background
The ETrade authentication system has me creating a RequestToken, then executing an Authorization URL, which opens an ETrade page.
The user logs in to authorize the activity on their account.
They receive a pin, which they enter in my app.
I call ExchangeRequestTokenForAccessToken with the RequestToken and the Pin.
Then we are off and running.
Question
The problem is I'm creating a service that runs continuously in the background. There won't be any user to log in. Conversely, I won't be making any trades. Just crunching numbers, looking for stocks that meet certain criteria.
I can't figure how to get this to work unattended.
Thanks, Brad.
Previously, I have used a series of WebRequests and manually added headers to simulate the authorization pages. This worked until about a year ago when ETrade complicated their headers with something that appears to be tracking information. I now use http://watin.org/ to log in, and to strip the Auth Code.
Sloppy code looks like this:
using WatiN.Core; // IE Automation
...
// verify current thread in STA.
Settings.Instance.MakeNewIeInstanceVisible = false;
var ieStaticInstanceHelper = new IEStaticInstanceHelper();
Settings.AutoStartDialogWatcher = false;
using (ieStaticInstanceHelper.IE = new IE())
{
string authCode = "";
ieStaticInstanceHelper.IE.GoTo(GetAuthorizationLink());
if (ieStaticInstanceHelper.IE.ContainsText("Scheduled System Maintenance"))
{
throw new ApplicationException("eTrade down for maintenance.");
}
TextField user = ieStaticInstanceHelper.IE.TextField(Find.ByName("USER"));
TextField pass = ieStaticInstanceHelper.IE.TextField(Find.ById("txtPassword"));
TextField pass2 = ieStaticInstanceHelper.IE.TextField(Find.ByName("PASSWORD"));
Button btn = ieStaticInstanceHelper.IE.Button(Find.ByClass("log-on-btn"));
Button btnAccept = ieStaticInstanceHelper.IE.Button(Find.ByValue("Accept"));
TextField authCodeBox = ieStaticInstanceHelper.IE.TextField(Find.First());
if (user != null && pass != null && btn != null &&
user.Exists && pass2.Exists && btn.Exists)
{
user.Value = username;
pass2.Value = password;
btn.Click();
}
btnAccept.WaitUntilExists(30);
btnAccept.Click();
authCodeBox.WaitUntilExists(30);
authCode = authCodeBox.Value;
SavePin(authCode);
}
Current version of Brad Melton's code.
WatiN has changed and no longer contains the IE.AttachToIE function.
So, IEStaticInstanceHelper is now called StaticBrowserInstanceHelper, but that code is hard to find, so I've included it here.
class StaticBrowserInstanceHelper<T> where T : Browser {
private Browser _browser;
private int _browserThread;
private string _browserHwnd;
public Browser Browser {
get {
int currentThreadId = GetCurrentThreadId();
if (currentThreadId != _browserThread) {
_browser = Browser.AttachTo<T>(Find.By("hwnd", _browserHwnd));
_browserThread = currentThreadId;
}
return _browser;
}
set {
_browser = value;
_browserHwnd = _browser.hWnd.ToString();
_browserThread = GetCurrentThreadId();
}
}
private int GetCurrentThreadId() {
return Thread.CurrentThread.GetHashCode();
}
}
ETrade's login pages have changed as well. They have several. All the login pages I checked consistently had a USER field and a PASSWORD field, but the login buttons had various names that look fragile. So if this doesn't work, that's the first thing I'd check.
Second, if I go directly to the auth page, it prompts to log in, but then it frequently doesn't take you to the auth page.
I got more consistent results by going to the home page to log in, then going to the auth page.
static public string GetPin(string username, string password, string logonLink, string authLink) {
// Settings.Instance.MakeNewIeInstanceVisible = false;
var StaticInstanceHelper = new StaticBrowserInstanceHelper<IE>();
Settings.AutoStartDialogWatcher = false;
// This code doesn't always handle it well when IE is already running, but it won't be in my case. You may need to attach to existing, depending on your context.
using (StaticInstanceHelper.Browser = new IE(logonLink)) {
string authCode = "";
// Browser reference was failing because IE hadn't started up yet.
// I'm in the background, so I don't care how long it takes.
// You may want to do a WaitFor to make it snappier.
Thread.Sleep(5000);
if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
throw new ApplicationException("eTrade down for maintenance.");
}
TextField user = StaticInstanceHelper.Browser.TextField(Find.ByName("USER"));
TextField pass2 = StaticInstanceHelper.Browser.TextField(Find.ByName("PASSWORD"));
// Class names of the Logon and Logoff buttons vary by page, so I find by text. Seems likely to be more stable.
Button btnLogOn = StaticInstanceHelper.Browser.Button(Find.ByText("Log On"));
Element btnLogOff = StaticInstanceHelper.Browser.Element(Find.ByText("Log Off"));
Button btnAccept = StaticInstanceHelper.Browser.Button(Find.ByValue("Accept"));
TextField authCodeBox = StaticInstanceHelper.Browser.TextField(Find.First());
if (user != null && btnLogOn != null &&
user.Exists && pass2.Exists && btnLogOn.Exists) {
user.Value = username;
pass2.Value = password;
btnLogOn.Click();
}
Thread.Sleep(1000);
if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
Element btnContinue = StaticInstanceHelper.Browser.Element(Find.ByName("continueButton"));
if (btnContinue.Exists)
btnContinue.Click();
}
btnLogOff.WaitUntilExists(30);
// Here we go, finally.
StaticInstanceHelper.Browser.GoTo(authLink);
btnAccept.WaitUntilExists(30);
btnAccept.Click();
authCodeBox.WaitUntilExists(30);
authCode = authCodeBox.Value;
StaticInstanceHelper.Browser.Close();
return authCode;
}
}
Being able to automate it like this means that I no longer care about how long the token is valid. Thanks BradM!
This was amazingly helpful. I used your code plus what was posted here to automate this (because tokens expire daily): E*Trade API frequently returns HTTP 401 Unauthorized when fetching an access token but not always
I made two edits:
Changed the authorize URL to what was posted here: https://seansoper.com/blog/connecting_etrade.html
For the log on button, changed it to search by ID: Button btnLogOn = StaticInstanceHelper.Browser.Button(Find.ById("logon_button"));
I ran into issues with Watin and setting up the Apartmentstate. So did this:
static void Main(string[] args)
{
System.Threading.Thread th = new Thread(new ThreadStart(TestAuth));
th.SetApartmentState(ApartmentState.STA);
th.Start();
th.Join();
}
Then put the code in the TestAuth method.

Authenticating with Facebook for Mobile Services in Azure

I am having trouble with facebook authentication for Mobile Services in Azure.
To be more specific, I already have an application that is using Facebook C# SDK and it works fine. I can log on, fetch list of my friends and so. I want to keep using this SDK, but I also want to authenticate for Azure Mobile Service.
So, my plan was, log on with Facebook C# SDK (as I already do today), get the authentication token, and pass it to the MobileServiceClient.LoginAsync() - function. That way, I can still have all the nice features in Facebook C# SDK, and also use the built in authentication system in Mobile Services for Azure.
var client = new FacebookClient();
dynamic parameters = new ExpandoObject();
parameters.client_id = App.FacebookAppId;
parameters.redirect_uri = "https://www.facebook.com/connect/login_success.html";
parameters.response_type = "token";
parameters.display = "popup";
var loginUrl = client.GetLoginUrl(parameters);
WebView.Navigate(loginUrl);
When load is complete, followin is executed:
FacebookOAuthResult oauthResult;
if (client.TryParseOAuthCallbackUrl(e.Uri, out oauthResult) && oauthResult.IsSuccess)
{
var accessToken = oauthResult.AccessToken;
var json = JsonObject.Parse("{\"authenticationToken\" : \"" + accessToken + "\"}");
var user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook, json);
}
However, I get this exception when I call the last line of code above:
MobileServiceInvalidOperationException, "Error: The POST Facebook login request must specify the access token in the body of the request."
I cannot find any information on how to format the accesstoken, I have tried a lot of different keys (instead of "authenticationToken" as you see in my sample). I also have tried just to pass the accesstoken string, but nothing seem to work.
Also, if I use the MobileServiceClient.LoginAsync() for making a brand new login, it works just fine, but it seem silly to force users to log on twice.
Any help is greatly appreciated!
The format expected for the object is {"access_token", "the-actual-access-token"}. Once the login is completed using the Facebook SDK, the token is returned in the fragment with that name, so that's what the Azure Mobile Service expects.
BTW, this is a code which I wrote, based on your snippet, which works. It should handle failed cases better, though, but for the token format, this should be enough
private void btnLoginFacebookToken_Click_1(object sender, RoutedEventArgs e)
{
var client = new Facebook.FacebookClient();
dynamic parameters = new ExpandoObject();
parameters.client_id = "MY_APPLICATION_CLIENT_ID";
parameters.redirect_uri = "https://www.facebook.com/connect/login_success.html";
parameters.response_type = "token";
parameters.display = "popup";
var uri = client.GetLoginUrl(parameters);
this.webView.LoadCompleted += webView_LoadCompleted;
this.webView.Visibility = Windows.UI.Xaml.Visibility.Visible;
this.webView.Navigate(uri);
}
async void webView_LoadCompleted(object sender, NavigationEventArgs e)
{
AddToDebug("NavigationMode: {0}", e.NavigationMode);
AddToDebug("Uri: {0}", e.Uri);
string redirect_uri = "https://www.facebook.com/connect/login_success.html";
bool close = (e.Uri.ToString().StartsWith(redirect_uri));
if (close)
{
this.webView.LoadCompleted -= webView_LoadCompleted;
this.webView.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
string fragment = e.Uri.Fragment;
string accessToken = fragment.Substring("#access_token=".Length);
accessToken = accessToken.Substring(0, accessToken.IndexOf('&'));
JsonObject token = new JsonObject();
token.Add("access_token", JsonValue.CreateStringValue(accessToken));
try
{
var user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook, token);
AddToDebug("Logged in: {0}", user.UserId);
}
catch (Exception ex)
{
AddToDebug("Error: {0}", ex);
}
}
}

Problems with own AD forms authentication in LS 2011 web application

I've got a problem with a LightSwitch 2011 web application using forms authentication.
I've implemented my own login screen which authenticates the user against the active directory. My code also checks to see if the user is assigned to a specific active directory group to decide if they can add / edit / delete data.
The login form is placed on the Login.aspx page. The button to login holds the following code:
protected void buttonLogin_Click(object sender, EventArgs e)
{
LdapAuthentication authentication = new LdapAuthentication();
try
{
bool isUserAdmin = false;
if (authentication.IsUserAuthenticated(textBoxUserName.Text, textBoxPassword.Text, ref isUserAdmin))
{
FormsAuthenticationTicket authenticationTicket = new FormsAuthenticationTicket(1,
textBoxUserName.Text, DateTime.Now, DateTime.Now.AddSeconds(1), false, String.Empty);
//Encrypt the ticket.
string encryptedTicket = FormsAuthentication.Encrypt(authenticationTicket);
//Create a cookie, and then add the encrypted ticket to the cookie as data.
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
//Add the cookie to the outgoing cookies collection.
Response.Cookies.Add(authCookie);
//If the everyoneAdmin is set to true the validation of the administratorgroup
//is decativated so we have to grant the current user administrator rights
if (everyoneAdmin)
isUserAdmin = true;
Session["isUserAdmin"] = isUserAdmin ;
Response.Redirect("default.htm");
}
}
catch (Exception ex)
{
labelError.Text = ex.Message;
labelError.Visible = true;
textBoxPassword.Text = String.Empty;
}
}
public bool IsUserAuthenticated(String userName, String password, ref bool isUserAdmin)
{
if (String.IsNullOrEmpty(userName) || String.IsNullOrEmpty(password))
return false;
String domain = String.Empty;
if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["Domain"]))
domain = Convert.ToString(ConfigurationManager.AppSettings["Domain"]).Trim();
else
throw new NullReferenceException("The Domain in the configuration must not be null!");
String ldpa = String.Empty;
if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["LDPA"]))
ldpa = String.Format("LDAP://{0}", Convert.ToString(ConfigurationManager.AppSettings["LDPA"]).Trim());
else
throw new NullReferenceException("The LDPA in the configuration must not be null!");
String administrationGroup = String.Empty;
if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["AdministratorGroup"]))
administrationGroup = Convert.ToString(ConfigurationManager.AppSettings["AdministratorGroup"]).Trim();
else
throw new NullReferenceException("The AdministrationGroup in the configuration must not be null!");
String domainUserName = String.Format(#"{0}\{1}", domain.Trim(), userName.Trim());
DirectoryEntry directoryEntry = new DirectoryEntry(ldpa, domainUserName, password);
try
{
//Bind to the native AdsObject to force authentication.
object obj = directoryEntry.NativeObject;
DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry);
directorySearcher.Filter = String.Format("(SAMAccountName={0})", userName.Trim());
directorySearcher.PropertiesToLoad.Add("cn");
directorySearcher.PropertiesToLoad.Add("memberOf");
SearchResult directorySearchResult = directorySearcher.FindOne();
//unable to find a user with the provided data
if (directorySearchResult == null)
return false;
if (directorySearchResult.Properties["memberof"] != null)
{
//If the memberof string contains the specified admin group
for (int i = 0; i < directorySearchResult.Properties["memberof"].Count; i++)
{
string temp = directorySearchResult.Properties["memberof"].ToString();
// get the group name, for example:
if (directorySearchResult.Properties["memberof"].ToString().ToLower().Contains(administrationGroup.ToLower()))
{
isUserAdmin = true;
break;
}
}
}
}
catch (Exception ex)
{
throw new Exception(String.Format("Error authenticating user.\n\rMessage:\n\r {0}", ex.Message));
}
return true;
}
In the class which holds the CanExcecute (server tier) methods I've implemented the following method:
public bool IsCurrentUserAdmin()
{
if (HttpContext.Current.Session["isUserAdmin"] == null)
return false;
return (bool)(HttpContext.Current.Session["isUserAdmin"]);
}
For example, the CanExcecute methods for one table
partial void dtFacilities_CanDelete(ref bool result)
{
result = this.IsCurrentUserAdmin();
}
partial void dtFacilities_CanInsert(ref bool result)
{
result = this.IsCurrentUserAdmin();
}
partial void dtFacilities_CanUpdate(ref bool result)
{
result = this.IsCurrentUserAdmin();
}
WebConfig
<authentication mode="Forms">
<form>s name=".ASPXAUTH"
loginUrl="Login.aspx"
protection="All"
timeout="30"
path="/"
requireSSL="false"
slidingExpiration="true"
defaultUrl="Home.aspx"
cookieless="UseUri" />
</authentication>
<authorization>
<deny users="?">
</deny></authorization>
Problems:
The problem is that if the user is idle for longer than the timeout the session times out. So, the session token isUserAdmin is NULL. At this point I want the application to return to the login screen. A Response.Redirect and a Server.Transfer did not work in the IsCurrentUserAdmin() method. How can I get the application to return the user to the login screen if the session token isUserAdmin is NULL?! Remember, the session token is set in the login.aspx page code behind
When the user closes the final tab of the Lightswitch application, the application opens a new tab and navigates past the login page and they are automatically logged in without processing the login process on the login.aspx page. This means that the session token isUserAdmin is NULL. This happens even if the user has not logged in before they closed the final tab of the application. This leads again to problem 1.
Thanks in advance!
If I understand your problem correctly, if, for whatever reason, isUserAdmin is set to NULL, you want to return the user to to the login screen.
In my application, I simply use a button that the user can click to log off. But the underlying method should work just the same in your case.
First create a new page called LogOff.aspx. The page itself, you can leave default generated code:
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
For the code behind, you'll want something like this (please check this, I converted from my project which is in VB):
using System.Web.Security;
namespace LightSwitchApplication
{
public partial class LogOff : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
FormsAuthentication.SignOut();
Response.Redirect("default.htm");
}
}
}
This is my code in which I use a button. But if you take the section where the Dispatcher calls Navigate and place it in your IsCurrentUserAdmin() method, it should do the same trick (again, check the C#):
using Microsoft.LightSwitch.Threading;
using System.Windows.Browser;
partial void btnLogOff_Execute()
{
Dispatchers.Main.Invoke(() =>
{
HtmlPage.Window.Navigate(new Uri("LogOff.aspx", UriKind.Relative));
});
}
In my experience, there is a bit of a gotcha in Lightswitch. If you were to execute as is, you would probably receive the following:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its
dependencies) could have been removed, had its name changed, or is
temporarily unavailable. Please review the following URL and make
sure that it is spelled correctly.
Requested URL: /LogOff.aspx
The fix is this:
First right click your project name in Solution Explorer and Unload Project. Once the project is unloaded, right click it and Edit project_name.lsproj. Ctrl+F for default.htm. You're looking for the section where it is proceeded by _BuildFile. Copy that section from _BuildFile to /_BuildFile, paste below that section and modify as follows.
<_BuildFile Include="Server/LogOff.aspx">
<SubFolder>
</SubFolder>
<PublishType>
</PublishType>
</_BuildFile>
Now right click and Reload your project. If you get errors when trying to build, try Build | Clean and build again. If you run the application in Debug, this code will just reload the page. But once you publish and subsequently cause isUserAdmin to be NULL the code should log you out and take you back to the log on screen.
References:
Original MSDN Forum Thread
My experience implementing it