In this section we are going to add a common Login View for our application, as well as a service that performs user authentication by email and password.
The final implementation of the authentication will depend on the specific technology we use, and we'll describe it for each technology in the subsequent sections.
Defining Authentication data object
As usual, we will begin with defining the relevant things in our Xomega model. Let's open up the
person.xom file, and declare a new data object called
AuthenticationObject, which will serve as a data model for our Login view.
Next we will define a new structure
credentials with two parameters for the
password, which will be added to our
AuthenticationObject as properties.
<param name="email" type="email" required="true"/>
<param name="password" type="plain password" required="true"/>
Notice how we use the
plain password type for the password parameter, which is pre-configured in the Xomega model to use a password field.
Customizing Authentication object
AuthenticationObject doesn't represent any persisted object, we will want to turn off modification tracking for it in a custom subclass, so we'll add a
Also, to configure the fields for the Login view, we will make them stack up in one column by setting
field-cols="1", and will set up
access-key values, which allow you to easily navigate to those fields using keyboard shortcuts.
The following snippet illustrates this setup.
<xfk:data-object class="AuthenticationObject" customize="true">
<ui:field param="email" access-key="m"/>
<ui:field param="password" access-key="P"/>
Adding Authenticate operation
Next let's add an operation
authenticate to the
person object, which will use the
credentials structure as an input argument. We also don't want to expose it via REST API, since the latter will use its own standard authentication mechanism, so we'll configure our operation to not support REST, as shown below.
<operation name="authenticate" type="update">
<input arg="credentials" struct="credentials"/>
<summary>Authenticates a Person using email and password.</summary>
Even though the operation is not going to update anything in the database, we marked it with
type="update", so that the Login View would be generated with a Save button plus the logic to call this operation with the supplied credentials, which is pretty much what we want for the Login button.
Defining Login view
So let's go ahead, and add the actual
LoginView definition in the model as follows.
<ui:view name="LoginView" title="Login" customize="true" child="true">
Let's have a look at some things that we have configured on our Login View. We will need to customize the logic for the generated view, so we marked it with the
customize="true" attribute. We also set the
child="true" attribute, so that this view would not be added to the main menu by the generator. And, of course, we set our
AuthenticationObject as the view model for the view.
These are all the changes that we need in the model, so we can go ahead and build the Model project now.
Custom service code for Authenticate
Before we provide a custom implementation for the
authenticate operation, let's open the
Resources.resx file under the
AdventureWorks.Services.Entities project, add the following message for invalid credentials, and run the custom tool on the nested
Messages.tt file to regenerate message constants.
Next, let's open the
PersonServiceCustomized.cs under the generated
PersonService.cs, and provide a custom implementation for the
AuthenticateAsync method as shown below.
public class PersonServiceCustomized : PersonService
public override async Task<Output> AuthenticateAsync(
Credentials _credentials, CancellationToken token = default)
// lookup password
var pwdQry = from em in ctx.EmailAddress
join pw in ctx.Password on em.BusinessEntityId equals pw.BusinessEntityId
where em.EmailAddress1 == _credentials.Email
var pwd = await pwdQry.FirstOrDefaultAsync(token);
// validate credentials
bool valid = false;
if (pwd != null && _credentials.Password != null)
valid = _credentials.Password.Equals("password"); // for testing only
// TODO: hash _credentials.Password using pwd.PasswordSalt,
// and compare it with pwd.PasswordHash instead
if (!valid) currentErrors.CriticalError(ErrorType.Security, Messages.InvalidCredentials);
return new Output(currentErrors);
This is where we would need to hash the user-supplied password using the salt that is stored in the database along with the hashed password, and compare the result with the latter. For ease of testing though, we will instead just compare it with a hardcoded word "password" for now.
If the email address is not found or if the password is not valid, we'll report a critical error using the generated constant
InvalidCredentials for our message.
Custom code for Authentication object
Next, let's open the generated
AuthenticationObjectCustomized.cs in the
AdventureWorks.Client.Common project, and set it up to not track modifications, as shown below. We also set the
IsNew flag to
false, so that the view title would say just Login instead of New Login.
public class AuthenticationObjectCustomized : AuthenticationObject
protected override void OnInitialized()
TrackModifications = false;
IsNew = false;
With this common code we'll have an internal operation that can authenticate a person by email and password, and a generated Login View that can collect this information and call that operation when the user clicks the Save button.
Presenting the Login view and the actual authentication of the user will need to be implemented for each specific technology, as we'll see in subsequent sections.
Before that though, let's see how you can write common platform-independent security logic in the business services and the presentation layer.