A pleasant walk through computing

Comment for me? Send an email. I might even update the post!

Google Tasks vs Reminders in Desktop and Phone

Google appears to be transitioning away from their dedicated Tasks app. This doesn't surprise me, as Tasks always seemed bolted on and only useful in the most minimal way, though some people--such as my wife--have used it effectively.

The general challenges of tasks are that they're more complex than people imagine. Some potential features:

  1. Scheduled vs non-scheduled tasks.
  2. Scheduled for anytime during the day, or for a time ("due by").
  3. Sub-tasks.
  4. Categories/tags.
  5. Movement (or not) of completed items to the bottom of the list.
  6. Clearing (or not) of completed items.

Google provides some reasonable task management apps.

Task Replacements

The two replacements for Tasks are:

  1. Keep
  2. Reminders

Tasks and Reminders work differently depending on whether you're using a desktop browser or your phone, which I'll detail later.

In Tasks, a task could be assigned a date, or "no due date." These loosely equate to...

  • Keep (no due date). Keep lets you create notes of checked lists. While Keep doesn't let you assign dates to a particular note's list item, it does let you set a reminder on a note.
  • Reminders (due date), which show on the Google Calendar for a particular date and, optionally, time.

Here's a Keep note with reminder dialog. You can also set location-based reminders, handy for a note like "drop off book to Sam."

image

And, here are two Reminders, from the desktop Calendar. One is "all day," the other is for 9am.

image

On the Desktop: Using Tasks OR Reminders

When accessing the Google Calendar from your desktop browser, you can view either Tasks or Reminders, but not both at once. Each is really a (somewhat) hidden calendar. To switch, click the down-arrow next to the Reminders (or Tasks) calendar under My Calendars.

image

When Reminders is selected, you'll see them as shown above. When Tasks is selected, you'll see a new sidebar, and scheduled tasks appear on the calendar.

image

There is a way to view Tasks in your browser without using Calendar. You use the same URL that your phone uses (see below). But who knows how long this will be available?

https://mail.google.com/tasks/android

 image

On the Phone: Viewing Tasks--but not Reminders--in the Browser

In Android (and probably iPhone), you can use the phone's web browser to access Gmail, the Calendar and Tasks. To view Tasks, go to:

https://mail.google.com/tasks

When the page loads, you'll see your tasks, regardless of whether you've chosen to view Reminders or Tasks in your desktop calendar. (As shown above, you can get to this same view from the desktop, using a slightly different URL.)

image

If you dropdown the "More" button, you can also view the Calendar. However, you will not see Tasks or Reminders, and I don't see a way to enable viewing them.

image

On the Phone Again: Viewing Reminders in the Calendar App

In the Calendar app, you can enable or disable showing Reminders, just as you would any other calendar. However, you cannot switch to the Tasks calendar. As far as I can find, there's no way to view Tasks using the Calendar App.

image

Here's a look at those Reminders.

image

Wrap Up

Google's Tasks are heading the way of the dodo, supplanted by Keep notes, and calendar Reminders. To summarize:

Desktop

  • In Google Calendar, you can toggle between either Tasks or Reminders.
  • If you want to be clever, you can set your Calendar for Reminders, and view Tasks at https://mail.google.com/tasks/android

Phone


Notes

* The difference between a task and an event? A task is something you do, an event is somewhere to be.


References

Google Tasks Help

Catching Up on Coding - Claims-Based Identity Part 1

the-magician

I’ve been a software developer for twenty years, but am behind in some skills. This series chronicles my self-improvement.

Claims-based identity isn't hard to understand, but it's been challenging to learn and implement in a legacy environment. In part one, we'll look at the concepts. In future posts, I'll have code samples using current (at the moment) frameworks.

Like Tony Blake producing a flock of doves, we'll learn some security sleight-of-hand.

Previously on CUOC: TDD Part 3
Next Episode: Claims-Based Identity Part 2

 

 

 

Claims-Based Identity Basics

What We're Used To - Passwords

The convention in authentication for decades--for millennia, in a sense--has been this exchange:

"Let me in."
"What's the password?"
"Swordfish."

You're allowed in because you know something only a few people should: the password. Once you're in, the exchange continues.

"I want to see the boss."
"Oh yeah? Who's asking?"
"I carry packages for Don Crosetti."
"A courier? Delivery boys don't see the boss, chum."

What you get to do is defined by your role. A lieutenant can see the boss. A courier can't.

This is very much how people understand software security to work. You enter a username and password on a web site, let's say a Content Management System. Once in the site, if you're a Viewer, you can look at all the pages but not edit anything. An Blogger can create posts for her own blogs, but not anyone else's. A Site Editor can modify anyone's posts.

This system works well enough for many situations (in both software and life). But there's another common approach: claims.

What We're Also Used To - Claims

It's Friday night and you go out to a dance club. To get in, you have to show your driver's license. The guy at the door looks it over, then waves you in. What was he checking? Two things.

  • Were you over 21?
  • Did the license look valid for the issuing state?

The first item is a claim. You're claiming to be over twenty-one. You have a driver's license issued by a recognized identity validator: the Bureau of Motor Vehicles. The doorman didn't verify your identity. He didn't ask for a social security card or birth certificate or fingerprints. He trusts that the BMV did all that. The most he did was check that the license was the expected color and had the right hologram.

As long as he trusted the license, he trusted the claims on the license: your birth date, name, height, weight, etc.

Roles can be claims: maybe at this particular club you have a VIP card that lets you into the upper floor. However, claims are not necessarily roles. Claims can allow more fine-grained and nuanced authorization.

There are two important aspects of this example:

  1. The bar didn't ask for a password. They trusted a third party's identity authentication.
  2. The bar authorized entry based on a claim, not a role.

With those two real-world examples in mind, we're ready to look at claims-based identity in software.

What's an Identity Provider (Security Token Service)?

In the bar example, the identity provider is the BMV.

An STS takes care of authenticating identity, if needed, and returns a token than contains claims about the identity. An STS could be local or remote. Examples include:

  • A local database containing user and password hashes.
  • A local Active Directory
  • Azure Active Directory
  • Google

What's a Token?

In the bar example, the token is the driver's license.

A token is just data about the identity and issuer. It could be an XML document, for instance. Generally speaking, it can have:

  • A unique ID
  • An issuer certificate id
  • A collection of claims

What's a Claim?

In the bar example, the claim is the age.

A claim is a fact about the identity (i.e., the user). A claim is trusted if the identity provider is trusted. Claims could include:

  • Name
  • Email
  • Date of Birth
  • Country

What's Federated Identity?

The word "federation" means "union by agreement."

A body formed by a number of nations, states, societies, unions, etc., each retaining control of its own internal affairs. [Dictionary.com]

When it comes to identity, otherwise unrelated organizations can trust each other's STSs, allowing a user to log in with one set of credentials that are accepted elsewhere. This forms the basis of single sign-on (SSO).

A simple example of real-world federation would be a background check company that wants to give its customers access to their reports without storing them as users. The company would:

  1. Set up a federated STS.
  2. Establish a trust with the customer's Active Directory (for example).
  3. The customer determines which AD claims to provide.

A customer's user would log into their computer using their corporate credentials. When she opened the background check web site, the site would check if there was an authenticate token, and find the user's credentials in a cookie (perhaps). The web site would contact the STS, which would contact the customer's AD and verify the token's validity and return claims. The site would then authorize access if the claims allowed.

At no time did the user retype credentials.

Simplest Steps of Getting Authenticated Claims - Code Neutral

Here's some pseudo-code (C# style) showing how an application might get and use a claims token. Note: These are not real .Net classes!

A simple user.

class User
{
    public string Email { get; set; }
}

The claims class. (The real .Net class is just about this simple.)

class Claim
{
    public string Type { get; set; }
    public string Value { get; set; }
}

A token/identity class, which contains a collection of claims.

class IdentityToken
{
    public string TokenId { get; set; }
    public IEnumerable<Claim> Claims { get; set; }
}

A helper class that checks authentication status.

static class AuthenticationManager
{
    public static bool IsAuthenticated(User user)
    {
        bool authState = false;
        //check a cookie, or database, or session state, etc.
        return authState;
    }

A provider service to handle getting the token. This is the complex part if you wrote it yourself, since it needs to properly manage the network connection, protocol, certificates, trust, etc.

class Provider
{
    public string SignOnUri { get; set; }
    public string ProviderId { get; set; }
    public string ApplicationId { get; set;}
    public IEnumerable<Claim> GetClaims(string email)
    {
        //Set up network connection, such as to a remote Azure AD server
        var connection = GetConnected();
        //The provider knows which applications are allowed.
        if (!connection.IsValidApp(applicationId)) { throw new Exception("Invalid app");}
        //The user might need to sign in via a remote page NOT on our site.     
        var response = connection.Authenticate(email, SignOnUri);
        if (response.Authenticated)
        {
            //The framework-specific provider takes care of the messy work of parsing the claims.
            var claims = new List<Claim>();
            foreach (string clm in response.TokenClaimCollection)
            {
                claims.Add(new Claim() { Name = clm.Identifier, Value = clm.ValueUri }
            }
            var token = new Token() { TokenId = response.TokenGuid, Claims = claims};
            //store the token info somewhere, like in a cookie or database
            Session.Store(token);
            return token.Claims;
        }
        else
        {
            throw new Exception("Not a valid user.");
        }
    }
}

And here's how authentication might look, using that provider.

void Main()
{
    //The user who wants access.
    User user = new User() { Email = "jane.doe@example.com" };
    //We have a class that checks if the user's authenticated.
    if (!AuthenticationManager.IsAuthenticated(user))
    {
        //Set up a service class that does the work of communicating
        //with the STS. Behind the scenes, it could use WS-Federation, OpenId, SAML, etc.
        var sts = new Provider()
        {
            SignOnUri = "https://login.otherplace.com",
            ProviderId = "ef26632f-9af6-4ea0-8fcc-a8cee466cb6d",
            ApplicationId = "mysite.mydomain.com/isunique"
        };
        //Get the claims collection in our format. If we get this without error,
        //the user was authenticated. We don't care how! This is where the user
        //might be redirected to a login page.
        var claims = sts.GetToken(user.Email).Claims;
        //Check if the user can do something
        if (claims.First(c => c.Name == "Company").Value == "XyloTech")
        {
            //Show their reports
        }
    }
}

In the above method, we

  1. Found the user wasn't authenticated
  2. Instantiated our identity provider. This included a way to verify to the provider who we were, and vice versa. (Note: technically, the application trusts the STS, but the STS doesn't trust the application.)
  3. Asked the provider to authenticate the user. We may not know or care how this happens.
  4. Got back claims. Our provider class did the messy work of converting it to our class collection.
  5. Checked for a specific claim and took action.

In the next post, we'll look at some of the various namespaces Microsoft uses for claims-based identity.

--------------------

Tony Blake's Corvette, license plate: "SPIRIT"

image

Creating Forms Authentication Membership Tables in LocalDB–Right and Wrong Ways

The Problem

There’s a rare chance these days that you’ll need to manually create the authentication tables used by the old ASP.NET WebForms Forms Authentication Membership Provider. The tool to do this is aspnet_regsql.exe , found in the .Net Framework folders.

Even rarer will be using the tool to create the tables in LocalDB. You don’t really have to. The database will be automatically created, even in Visual Studio 2015, if you do the steps at the end of the article.

The Wrong Way

But let’s say you do use the tools, as in this command line for Windows 64-bit.

%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regsql.exe -E -S (LocalDB)\v11.0 -d aspnetdb -A mr

Assuming you have the SQL 2012 LocalDB installed, running the above will create a file in your user profile named aspnet.mdf, for example C:\Users\charles\aspnetdb.mdf.

Here’s the rub. If you delete the file, then run the command again, you’ll get this error.

An error occurred during the execution of the SQL file 'InstallCommon.sql'. The SQL error number is 5120 and the SqlException message is: Unable to open the physical file "C:\Users\charles\aspnetdb.mdf".

This happens because LocalDB is still storing an entry in sys.databases (in the master database) for the database you just created. In my opinion, it shouldn’t, but what do I know?

Cleaning Up from the Wrong Way

To remove this entry, you need to delete the entry by connecting to LocalDB like a server. These instructions show using Visual Studio’s database tools, but the steps are similar for SQL Management Studio.

  1. Open Visual Studio
  2. You don’t need to create a project. Just Tools > Connect to Database.
    image
  3. Choose Data source = Microsoft SQL Server
  4. Enter Server name = (LocalDB)\v11.0
  5. Drop down the list of databases. You should at least see master, etc.
    image
  6. You will not see aspnetdb. Choose the master database.
  7. Open the Server Explorer, right-click the connection and choose New Query.
    image
  8. In the query window, enter and run:
    select * from sysdatabases
  9. You’ll see aspnetdb listed.
    image
  10. Run this command:
    drop database aspnetdb
  11. You’ll get an error message:
    Unable to open the physical file "C:\Users\charles\aspnetdb.mdf". Operating system error 2: "2(The system cannot find the file specified.)".
    File activation failure. The physical file name "C:\Users\charles\aspnetdb_log.ldf" may be incorrect.
  12. But that’s OK. If  you select from sysdatabases again, you’ll see the aspnetdb has been removed.
    image

Creating the Database the Right (Easy) Way

I didn’t find documentation saying I could do this, and tried it on a whim. Basically, if you configure your project to use a LocalDB as your membership provider, the first time the app tries to use it the database will be created with the correct tables. At least, this is true in Visual Studio 2015.

Add Old-Style Forms Authentication

Web.Config

Open web.config. Under <configuration>, add this connection string.

  <connectionStrings>
    <!-- ASP.NET MembershipProvider-->
    <add name="ASPNETDB" connectionString="Data Source=(LocalDB)\v11.0; Integrated Security=true;AttachDbFileName=|DataDirectory|ASPNETDB.mdf" providerName="System.Data.SqlClient" />
  </connectionStrings>

In <system.web>, add this.

     <!--ASP.NET MembershipProvider, must be None for other providers such as OWIN-->
    <authentication mode="Forms">
      <forms loginUrl="~/" timeout="2880" />
    </authentication>
    <membership defaultProvider="AspNetSqlMembershipProvider">
      <providers>
        <clear />
        <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ASPNETDB" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
      </providers>
    </membership>
    <!--Set enabled to false for OWIN-->
    <roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider">
      <providers>
        <clear />
        <add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ASPNETDB" applicationName="/" />
        <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
      </providers>
    </roleManager>

Create the Membership Database

In Default.aspx.cs

Add these using statements.

//MembershipProvider (forms authentication)
using System.Security.Principal;
using System.Web.Security;

In some part of your app’s startup, add this code, which we'll run once to create the database, add a user, and add a role. For example, this could go in a Web Form’s Page_Load, in a Controller action, or in a console app’s Main. You could, of course, not create a role, or delete the user afterward, etc.

            string email = "charles@aspnetmembership.com";
            string role = "Dev";

            Membership.CreateUser(email, "BadPassword!", email);
            if (!Roles.RoleExists(role))
            {
                Roles.CreateRole(role);
            }
            Roles.AddUserToRole(email, role);

Run the project. The aspnetdb.mdf database file will be created in App_Data, with the needed tables.

Delete the code added above.