Home > Mobile >  I want to show multiple pages with routing when user is not Authorized in Blazor
I want to show multiple pages with routing when user is not Authorized in Blazor

Time:08-05

I have App.razor file looking like this:

@using MyFirstServerSideBlazor.Pages

<CascadingAuthenticationState>
   <Router AppAssembly="@typeof(App).Assembly">
      <Found Context="routeData">
         <AuthorizeView>
            <Authorized>
               <RouteView DefaultLayout="@typeof(MainHeaderLayout)" RouteData="@routeData" />
            </Authorized>
            <NotAuthorized>               
               <RouteView DefaultLayout="@typeof(LoginBody)" RouteData="@routeData" />
            </NotAuthorized>
         </AuthorizeView>        
      </Found>
      <NotFound>
         <PageTitle>Not found</PageTitle>
         <p role="alert">Sorry, there's nothing at this address.</p>
      </NotFound>
   </Router>
</CascadingAuthenticationState>

Is there some way I can specify what routes I want to show, for Unauthenticated user ?

Lets say I want to let Unauthenticated users to navigate to /Login & /SignUp pages. I could probable use attributes or something on pages to specify that only authenticated user can view them, but this seems to be inconvenient. I'm wondering is there some better solution ?

I could also make component that shows different pages, but Im unable to change URI routes that way, example would be:

<NotAuthorized>               
   <ComponentForUnauthorizedUsers/>
</NotAuthorized>

And then in that component have some logic like this:

@using MyFirstServerSideBlazor.Componenets.Forms

@inject NavigationManager navigation

@{
   if (ShowLogin)
   {
      <PageTitle>Login</PageTitle>
   }
   else
   {
      <PageTitle>Sign Up</PageTitle>
   } 
}

@{
   if (ShowLogin)
   {
      <LoginForm ChangePageCallback="ChangeToSignUp"/>
   }
   else
   {
      <SignUpComponent ChangePageCallback="ChangeToLogin"/>
   }
}

@code{
   public bool ShowLogin { get; set; } = true;

   public void ChangeToSignUp()
   {
      ShowLogin = false;
      StateHasChanged();
   }    

   public void ChangeToLogin()
   {
      ShowLogin = true;
      StateHasChanged();
   }    
}

this also seems wrong, and this way I cannot use Routing.

CodePudding user response:

There's a handful of options here and I wish I knew the best one myself. Here's what I'm currently doing in my app:

App.Razor

@inject NavigationManager nav

<CascadingAuthenticationState>
    <AuthorizeView>
        <NotAuthorized>
            @{
                /* THE IMPORTANT PART TO REDIRECT AWAY FROM UNAUTHORIZED PAGES */
                if (new Uri(nav.Uri).AbsolutePath.StartsWith("/login") == false)
                    nav.NavigateTo($"/login/{System.Web.HttpUtility.UrlEncode(new Uri(nav.Uri).PathAndQuery)}");
            }
        </NotAuthorized>
    </AuthorizeView>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <Authorizing>
                    <RequestCardTemplate Title="Loading...">
                        <Body>
                            Loading...
                        </Body>
                    </RequestCardTemplate>
                </Authorizing>
            </AuthorizeRouteView>
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <h1 >Sorry, there's nothing at this address.</h1>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>

Deciding which pages require auth

If a page does not require authorization, then I just write the page as normal. If it does require authorization, then I drop the Authorize attribute into the MyPage.razor file.

@page "/mypage"
@attribute [Authorize]

@* Rest of page stuff *@

Different components

As you mentioned, you can show different links or something if you want to based on whether a user is logged in (authorized?) or not.

@* page stuff... *@

<AuthorizeView>
    <Authorized>
        <a href="/myaccount">My Account</a>
        <a href="/myposts">My Posts</a>
    </Authorized>
    <NotAuthorized>
        <a href="/login">Login</a>
        <a href="/signup">Signup</a>
    </NotAuthorized>
</AuthorizeView

@* more page stuff...

CodePudding user response:

Okay with few tweaks it I managed to make it Work, In my APP.Razor:

<CascadingAuthenticationState>
  <Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
      <AuthorizeView>
        <Authorized>
          <RouteView DefaultLayout="@typeof(MainHeaderLayout)" RouteData="@routeData" />
        </Authorized>
        <NotAuthorized>
          <UnauthorizedLayout/>
        </NotAuthorized>
      </AuthorizeView>        
    </Found>
    <NotFound>
      <PageTitle>Not found</PageTitle>
      <p role="alert">Sorry, there's nothing at this address.</p>
    </NotFound>
  </Router>
</CascadingAuthenticationState>

Then in Layout I have This:

@inherits LayoutComponentBase

<div >
  <UnauthorizedViewControler/>
</div>

In UnauthorizedViewControler component i Have this logic:

@inject NavigationManager navigation

@{
  if (ShowLogin)
  {
    <LoginPage/>
  }
  else
  {
    <SignUpPage/>
  }
}

@code{
  public bool ShowLogin { get; set; } = true;

  protected override void OnInitialized()
  {
    LocationChanged(null,null);
    navigation.LocationChanged  = LocationChanged;
    base.OnInitialized();
  }

  void LocationChanged(object sender, LocationChangedEventArgs e)
  {       
    if (navigation.Uri.Contains("/signup"))
    {
      ShowLogin = false;
    }
    else
    {
      ShowLogin = true;
    }

    StateHasChanged();
  }   
}

And for pages themselves :

// injects & usings...

@layout UnauthorizedLayout
@page "/Login" 

// HTML & Code...

,

// injects & usings...

@layout UnauthorizedLayout
@page "/SignUp" 

// HTML & Code...

And this works, I have routing for /Login and /SignUp, Code, could use some cleaning up, but in principle this works.

  • Related