Skip to content

DevExpress-Examples/maui-role-based-data-access

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Role-Based Data Access with the DevExpress Web API Service

This example uses our free .NET App Security & Web API Service to implement authentication and role-based data access. We ran a built-in wizard to generate a ready-to-use authentication service. This service uses Entity Framework Core to access a database. A .NET MAUI application sends requests to the Web API Service to obtain or modify data.

If you are new to the DevExpress .NET App Security & Web API Service, you may want to review the following resources:

Create a Standalone Web API Application

A 1-Click Solution for CRUD Web API with Role-based Access Control via EF Core & ASP.NET

Prerequisites

SQL Server, if you run this solution on Windows.

Run Projects

  1. Run Visual Studio as Administrator and open the solution. Administrator privileges allow the IDE to create a database when you run the Web Service project.

  2. Select WebApi in the debug drop-down menu. This choice enables Kestrel as the web server for debug runs.

    image

    If you prefer IIS Express to Kestrel, select IIS Express in the debug drop-down menu. Use an external text editor to add the following code to .vs\MAUI_WebAPI\config\applicationhost.config:

    <sites>
        <site name="WebSite1" id="1" serverAutoStart="true">
        <!-* ... -->
            <bindings>
                <binding protocol="http" bindingInformation="*:65201:*" />
                <binding protocol="https" bindingInformation="*:44317:*" />
                <binding protocol="https" bindingInformation="*:44317:localhost" />
                <binding protocol="http" bindingInformation="*:65201:localhost" />
            </bindings>
        </site>
        <!-* ... -->
    </sites>
  3. Right-click the MAUI project, choose Set as Startup Project, and select your emulator. Note that physical devices that are attached over USB cannot access your machine's localhost.

  4. Right-click the WebAPI project and select Debug > Start new instance.

  5. Right-click the MAUI project and select Debug > Start new instance.

Implementation Details

Service and Communication

  • DevExpress Web API Service uses JSON Web Tokens (JWT) to authorize users. To obtain a token, pass username and password to the Authenticate endpoint. In this example, token generation logic is implemented in the WebAPIService.RequestTokenAsync method:

      private async Task<HttpResponseMessage> RequestTokenAsync(string userName, string password) {
            return await HttpClient.PostAsync($"{ApiUrl}Authentication/Authenticate",
                                                new StringContent(JsonSerializer.Serialize(new { userName, password = $"{password}" }), Encoding.UTF8,
                                                ApplicationJson));
      }

    Include the token in HttpClient.DefaultRequestHeaders.Authorization. All subsequent requests can then access private endpoints and data: 

    HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await tokenResponse.Content.ReadAsStringAsync());

    File to Look At: WebAPIService.cs

  • We implemented the following custom endpoints in the WebApi service:

    • The CanDeletePost endpoint allows you to send a request from a mobile device to the service and check whether the current user can delete posts. This allows you to show/hide the delete button in the UI.

      File to Look At: Updater.cs

    • The CurrentUser endpoint returns information about the authenticated user.

      File to Look At: Updater.cs

    • The GetAuthorImage endpoint retrieves an author image by user ID.

      File to Look At: Updater.cs

    • The GetPostImage endpoint retrieves an image by post ID.

      File to Look At: Updater.cs

  • The Updater.UpdateDatabaseAfterUpdateSchema method generates users and specifies their login credentials. You can modify a user's password directly in the database. Note: Our cross-platform .NET Application Framework (XAF UI) allows you to quickly build a desktop or web UI that accesses the same database.

    File to Look At: Updater.cs

  • PermissionPolicyRole objects in the Updater class add user permissions. The following code snippet calls the AddObjectPermissionFromLambda method to configure the "Viewer" role (allow the user to read published posts):

    role.AddObjectPermissionFromLambda(SecurityOperations.Read, p => p.IsPublished, SecurityPermissionState.Allow);

    File to Look At: Updater.cs

  • The AddTypePermissionsRecursively method modifies privileges for the "Editor" role (Alex, Antony, and Dennis). The method adds CRUD permissions (create, read, update, delete) for the Post type:

    role.AddTypePermissionsRecursively<Post>(SecurityOperations.Read | SecurityOperations.Write | SecurityOperations.Create | SecurityOperations.DeleteObject, SecurityPermissionState.Allow);

    File to Look At: Updater.cs

Login UI and View Model

  • Use TextEdit.StartIcon and PasswordEdit.StartIcon properties to display icons in TextEdit and PasswordEdit controls.

    <dxe:TextEdit LabelText="Login" StartIcon="editorsname" .../>
    <dxe:PasswordEdit LabelText="Password" StartIcon="editorspassword" .../>

    File to Look At: LoginPage.xaml

  • To validate user input in the PasswordEdit control, use EditBase.HasError and EditBase.ErrorText inherited properties.

    <dxe:PasswordEdit ... HasError="{Binding HasError}" ErrorText="{Binding ErrorText}"/>

    File to Look At: LoginPage.xaml

    public class LoginViewModel : BaseViewModel {
        // ...
        string errorText;
        bool hasError;
        // ...
    
        public string ErrorText {
            get => errorText;
            set => SetProperty(ref errorText, value);
        }
    
        public bool HasError {
            get => hasError;
            set => SetProperty(ref hasError, value);
        }
        async void OnLoginClicked() {
            /// ...
            string response = await DataStore.Authenticate(userName, password);
            if (!string.IsNullOrEmpty(response)) {
                ErrorText = response;
                HasError = true;
                return;
            }
            HasError = false;
            await Navigation.NavigateToAsync<SuccessViewModel>();
        }
    }

    File to Look At: LoginViewModel.cs

  • Specify the TextEdit.ReturnType inherited property to focus the PasswordEdit control after the TextEdit control's value is edited.

  • Use the PasswordEdit.ReturnCommand property to specify a command (Login) that runs when a user enters the password:

    <dxe:PasswordEdit ReturnCommand="{Binding LoginCommand}"/>

    File to Look At: LoginPage.xaml

    public class LoginViewModel : BaseViewModel {
        // ...
        public LoginViewModel() {
            LoginCommand = new Command(OnLoginClicked);
            SignUpCommand = new Command(OnSignUpClicked);
            PropertyChanged +=
                (_, __) => LoginCommand.ChangeCanExecute();
    
        }
        // ...
        public Command LoginCommand { get; }
        public Command SignUpCommand { get; }
        // ...
    }

    File to Look At: LoginViewModel.cs

  • We enabled image caching in this project. To achieve that, we needed to identify images by their Uri. To create a Uri, we use a MultiBinding that obtains the host name and the author/post ID. For additional information on image caching, refer to MAUI documentation.

    <dx:DXImage>
        <dx:DXImage.Source>
            <MultiBinding StringFormat="{}{0}PublicEndpoint/PostImage/{1}">
                <Binding Source="{x:Static webService:WebAPIService.ApiUrl}"/>
                <Binding Path="PostId"/>
            </MultiBinding>
        </dx:DXImage.Source>
    </dx:DXImage>

    File to Look At: ItemsPage.xaml

Debug Specifics

Android emulator and iOS simulator request a certificate to access a service over HTTPS. In this example, we switch to HTTP in debug mode:

#if !DEBUG
    app.UseHttpsRedirection();
#endif

MAUI - Android

<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">10.0.2.2</domain>
    </domain-config>
</network-security-config>

MAUI - iOS

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsLocalNetworking</key>
    <true/>
</dict>

This allows you to bypass the certificate check without the need to create a development certificate or implement HttpClient handlers.

For more information, please refer to Connect to local web services from Android emulators and iOS simulators.

We recommend that you use HTTP only when you develop/debug your application. In production, use HTTPS for security reasons.

Files to Look At

Documentation

More Examples