Async method not executing from within razor component - asp.net-core

I have a server-side Blazor app, and in 'app.razor' I have:
<CascadingAuthenticationState>
<Router AppAssembly="#typeof(Program).Assembly" PreferExactMatches="#true">
<Found Context="routeData">
<AuthorizeRouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)">
<NotAuthorized>
#(myService.Login())
</NotAuthorized>
<Authorizing>
<p>Bitte warten...</p>
</Authorizing>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="#typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
This is my service:
public class MyService : IMyService
{
protected readonly HttpClient httpClient;
public MyService(
HttpClient httpClient)
{
this.httpClient = httpClient;
}
public async Task Login()
{
await httpClient.GetAsync(loginUrl);
}
}
And the service is registered as follows:
services.AddHttpClient<IMyService, MyService>(client =>
{
client.BaseAddress = new Uri(someUrl);
});
However, I get the following error on running:
System.Runtime.CompilerServices.AsyncTaskMethodBuilder1+AsyncStateMachineBox1[System.Threading.Tasks.VoidTaskResult,App.Services.MyService+d__5]
What am I doing wrong?

Lots...
This is how NotAuthorized should look like:
<NotAuthorized>
#if (!context.User.Identity.IsAuthenticated)
{
<RedirectToLogin />
}
else
{
<p>You are not authorized to access this resource.</p>
}
</NotAuthorized>
In the if clause you check if the user is authenticated. If not you redirect him to the RedirectToLogin component. You do not call a service method here.
In the else clause you display a message... This is done because a user can be authenticated, but he's still not authorized to access a resource.
RedirectToLogin.razor
#inject myService myService
Put here html elements to gather the users' credentials with a "Login"
button, which when clicked, calls myService.Login() method to autenticate the
user.
#code
{
// Note: this code sample demonstrate how to login a user when you use
// Asp.Net Core Identity. It is not clear what identity system are you using
protected override void OnInitialized()
{
ReturnUrl = "~/" + ReturnUrl;
NavigationManager.NavigateTo($"Identity/Account/Login?returnUrl=
{ReturnUrl}", forceLoad:true);
}
}
Note: This expression: #(myService.Login()) is evaluated to System.Threading.Tasks.VoidTaskResult which causes the error. You should use return await httpClient.GetAsync(loginUrl); instead

Related

Why am i getting a status 400 after submitting my form?

I am trying to develop a simple app with Next.js but i am having issues with learning how to create an api.
This is my component to add an event with a form and a submit method that fetchs the data from the api.
import React from 'react'
import Layout from '#/components/Layout'
import styles from '#/styles/AddEvent.module.css'
export default function AddEventPage() {
const submitHanlder = (e) => {
e.preventDefault();
const formData = {
title: e.target.title.value,
description: e.target.description.value
}
fetch('/api/events', {
method: 'POST',
body: JSON.stringify(formData)
});
console.log(formData)
}
return (
<Layout title='Add New Event'>
<h1>Add Event</h1>
<div className={styles.container}>
<form className={styles.form} action="" onSubmit={submitHanlder}>
<label className={styles.label} >Title</label>
<input type="text" name="title" />
<label className={styles.label} >Description</label>
<input type="text" name="description"/>
<label className={styles.label}htmlFor="">Date</label>
<input type="date" />
<button type='submit' >Submit</button>
</form>
</div>
</Layout>
)
}
And this is my api/events.js:
const handler = (req , res) => {
if(req.method === 'POST'){
return res.status(201).json({ message: 'evento agregado' });
}
return res.status(400).json({ error: 'no se pudo agregar el evento' });
}
export default handler;
It is a simple handler that returns a message depending on the request method, but i am always getting the status 400 with the error message.
This is my request in the browser console:
{"title":"asdasd","description":"asdasdas"}
And my response:
message "evento agregado"
So it seems that is working. But when i go to localhost:3000/api/events it shows the following:
error "no se pudo agregar el evento"
You could try to print out what req.method is when the api gets called.
Add the following statement:
console.log(req.method)`
Then have a look at what the server logs out.
When you try to open localhost:3000/api/events in your browser the browser makes a GET Request, not a POST Request, to your api. That is the default behavior when opening a 'website'.
If you want to test your api you could use Postman or an extension for Visual Studio Code. The REST Client from Visual Studio Marketplace would probably be an option.

Translate Blazor AuthorizeView into code behind

I'm using the AutorizeView based on Roles in a blazor page to show/hide some features. But I need to do the same thing in the code behind. I don't know how to do it. In my particular case, If the logged user were admin it will see a complete list of Patients and, if it's not, it will see only the patients assigned to him.
<AuthorizeView Roles="Administrator" Context="search">
<Authorized>
random text
</Authorized>
</AuthorizeView>
I found this solution, but I don't use Policies. I'm just using Roles="Administrator".
You can use the cascading authentication state to get the current user's claims principal.
#code {
[CascadingParameter]
private Task<AuthenticationState> authenticationStateTask { get; set; }
protected override async Task OnInitializedAsync()
{
var authState = await authenticationStateTask;
var user = authState.User;
if (user.IsInRole("Administrator"))
{
// load all patients
}
else
{
// load specific patients
}
}
}
You have to add CascadingAuthenticationState and AuthorizeRouteView components inside App.razor:
<CascadingAuthenticationState>
<Router AppAssembly="#typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="#routeData"
DefaultLayout="#typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="#typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-6.0#expose-the-authentication-state-as-a-cascading-parameter

VueJS - can't get user token from Django Rest Framework via login POST

I have token authentication setup with DRF and VueJS. However, I can't seem to login using the combo of VueJS, axios, and the DRF token system and I don't understand why. I have CORS installed and if I manually set the token into the VueJS code, all my requests from VueJS to the DRF backend API work fine. It's just the login and getting the initial token that isn't working.
Here's my code:
Django URLs:
from rest_framework.authtoken import views
url(r'^api/token-auth/', views.obtain_auth_token)
Django middleware
class SiteLogin:
def process_request(self, request):
url = request.path.split('/')
# Don't require password for the URL /api/token-auth/
if url[1] == 'api' and url[2] == 'token-auth':
return None
auth Service -- VueJS:
// ... other code ...
login (context, creds, redirect) {
console.log(creds)
axios.post(LOGIN_URL, creds).then((response) => {
localStorage.setItem('token', response.data.token)
this.user.authenticated = true
// If a redirect link is provided
if (redirect) {
router.push(redirect)
}
}).catch((err) => {
console.log(err)
})
},
Login Component - VueJS:
<template>
<div>
<div class='container-fluid container-fullw bg-white'>
<div class='row'>
<div class='col-md-4'>
Login<br>
<br>
<input type='text' class='form-control' placeholder="Username" v-model='credentials.username'><br>
<input type='password' class='form-control' placeholder="Password" v-model='credentials.password'><br>
<button class='btn btn-primary btn-sm' #click='submit()'>Login</button>
</div>
</div>
</div>
</div>
</template>
<script>
import auth from '../../services/auth'
export default {
name: 'login',
data () {
return {
credentials: {
username: '',
password: ''
},
error: ''
}
},
methods: {
submit () {
auth.login(this, this.credentials, '/')
}
}
}
</script>
Upon clicking "Submit" the DRF backend always returns a 401.
Edit:
Also, if I add print(request.POST) into the SiteLogin section of the Django middleware, I can see that a) the if statement returns true (it's acknowledging that it's a login URL) and b) the QueryDict is empty -- I see: <QueryDict: {}>
I solved it over here: https://stackoverflow.com/a/45850775/717682
Problem was that the TokenAuthentication was preventing my axios POST request from being called at all -- immediately returning a 401.

emberjs component form action

I'm trying to authenticate against DRF token.
I've successfully been able to login using an auth app I have created.
I thought I'd be slick and make the login form a component.
Since making it a component however, I'm not able to login and I get an Assertion failure.
My templates/components/auth-login.hbs template looks like so ...
<form class='navbar-form navbar-right' {{action 'authenticate' on='submit'}}>
<div class="form-group">
{{input id='identification' placeholder='Username' type='text' class='form-control' value=identification}}
{{input id='password' placeholder='Password' type='password' class='form-control' value=password}}
</div>
<button type="submit">Login</button>
</form>
I also have app/controllers/auth-login.js
import Ember from 'ember';
export default Ember.Controller.extend({
session: Ember.inject.service(),
actions: {
authenticate: function() {
var credentials = this.getProperties('identification', 'password'),
authenticator = 'authenticator:jwt';
this.get('session').authenticate(authenticator, credentials).catch((reason) => {
this.set('errorMessage', reason.error || reason);
});
}
}
});
It works as an app but not as a component.
If I blank the template, and use the auth route/app instead, it works peachy.
Option 1. You need to define action authenticate in actions hash of auth-login component.
Option 2. You can keep identification, password properties and authenticate action in controller. and include the auth-component like below,
app/templates/application.hbs
{{auth-component identification=identification password=password authenticate="authenticate" }}
app/components/auth-component.js
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
authenticate() {
this.sendAction('authenticate'); //this will call corresonding controller authenticate method through bubbling.
}
}
});
app/controllers/application.js
import Ember from 'ember';
export default Ember.Controller.extend({
session: Ember.inject.service(),
actions: {
authenticate: function() {
var credentials = this.getProperties('identification', 'password'),
authenticator = 'authenticator:jwt';
this.get('session').authenticate(authenticator, credentials).catch((reason) => {
this.set('errorMessage', reason.error || reason);
});
}
}
});

Hiding routes in Aurelia nav bar until authenticated

Is there a proper way to hide items in the Aurelia getting started app behind some authentication.
Right now I'm just adding a class to each element based on a custom property. This feels extremely hacky.
<li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}${!row.isVisible ? 'navbar-hidden' : ''}">
<a href.bind="row.href">${row.title}</a>
</li>
There are two directions you can take here.
The first is to only show nav links in the nav bar when the custom property is set like you are. To clean it up a bit let's use the show binding -
<li repeat.for="row of router.navigation" show.bind="isVisible" class="${row.isActive ? 'active' : ''}">
<a href.bind="row.href">${row.title}</a>
</li>
The issue here is you still need to maintain the custom property like you are already doing. The alternative is to reset the router. This basically involves building out a set of routes that are available when the user is unauthenticated and then a separate set once the user is authenticated -
this.router.configure(unauthenticatedRoutes);
// user authenticates
this.router.reset();
this.router.configure(authenticatedRoutes);
This gives you the flexibility to reconfigure the router whenever you need to.
These answers are great, though for the purposes of authentication, I don't think any have the security properties you want. For example, if you have a route /#/topsecret, hiding it will keep it out of the navbar but will not prevent a user from typing it in the URL.
Though it's technically a bit off topic, I think a much better practice is to use multiple shells as detailed in this answer: How to render different view structures in Aurelia?
The basic idea is to send the user to a login application on app startup, and then send them to the main app on login.
main.js
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging();
// notice that we are setting root to 'login'
aurelia.start().then(app => app.setRoot('login'));
}
app.js
import { inject, Aurelia } from 'aurelia-framework';
#inject(Aurelia)
export class Login {
constructor(aurelia) {
this.aurelia = aurelia;
}
goToApp() {
this.aurelia.setRoot('app');
}
}
I've also written up an in-depth blog with examples on how to do this: http://davismj.me/blog/aurelia-login-best-practices-pt-1/
Although I like PW Kad's solution (it just seems cleaner), here's an approach that I took using a custom valueConvertor:
nav-bar.html
<ul class="nav navbar-nav">
<li repeat.for="row of router.navigation | authFilter: isLoggedIn" class="${row.isActive ? 'active' : ''}" >
<a data-toggle="collapse" data-target="#bs-example-navbar-collapse-1.in" href.bind="row.href">${row.title}</a>
</li>
</ul>
nav-bar.js
import { bindable, inject, computedFrom} from 'aurelia-framework';
import {UserInfo} from './models/userInfo';
#inject(UserInfo)
export class NavBar {
#bindable router = null;
constructor(userInfo){
this.userInfo = userInfo;
}
get isLoggedIn(){
//userInfo is an object that is updated on authentication
return this.userInfo.isLoggedIn;
}
}
authFilter.js
export class AuthFilterValueConverter {
toView(routes, isLoggedIn){
console.log(isLoggedIn);
if(isLoggedIn)
return routes;
return routes.filter(r => !r.config.auth);
}
}
Note the following:
Your isLoggedIn getter will be polled incessantly
You can achieve the same with an if.bind="!row.config.auth || $parent.isLoggedIn" binding, but make sure that your if.bind binding comes after your repeat.for
I realize this is a bit of thread necromancy, but I wanted to add an answer because the accepted answer offers a solution that's explicitly recommended against by the Aurelia docs (you have to scroll down to the reset() method.
I tried several other methods, to varying degrees of success before I realized that I was looking at it wrong. Restriction of routes is a concern of the application, and so using the AuthorizeStep approach is definitely the way to go for blocking someone from going to a given route. Filtering out which routes a user sees on the navbar, though, is a viewmodel concern in my opinion. I didn't really feel like it was a value converter like #MickJuice did, though, as every example I saw of those were about formatting, not filtering, and also I felt like it's a bit cleaner / more intuitive to put it in the nav-bar view model. My approach was as follows:
// app.js
import AuthenticationService from './services/authentication';
import { inject } from 'aurelia-framework';
import { Redirect } from 'aurelia-router';
#inject(AuthenticationService)
export class App {
constructor(auth) {
this.auth = auth;
}
configureRouter(config, router) {
config.title = 'RPSLS';
const step = new AuthenticatedStep(this.auth);
config.addAuthorizeStep(step);
config.map([
{ route: ['', 'welcome'], name: 'welcome', moduleId: './welcome', nav: true, title: 'Welcome' },
{ route: 'teams', name: 'teams', moduleId: './my-teams', nav: true, title: 'Teams', settings: { auth: true } },
{ route: 'login', name: 'login', moduleId: './login', nav: false, title: 'Login' },
]);
this.router = router;
}
}
class AuthenticatedStep {
constructor(auth) {
this.auth = auth;
}
run(navigationInstruction, next) {
if (navigationInstruction.getAllInstructions().some(i => i.config.settings.auth)) {
if (!this.auth.currentUser) {
return next.cancel(new Redirect('login'));
}
}
return next();
}
}
OK, so that by itself will restrict user access to routes if the user isn't logged in. I could easily extend that to something roles based, but I don't need to at this point. The nav-bar.html then is right out of the skeleton, but rather than binding the router directly in nav-bar.html I created nav-bar.js to use a full view-model, like so:
import { inject, bindable } from 'aurelia-framework';
import AuthenticationService from './services/authentication';
#inject(AuthenticationService)
export class NavBar {
#bindable router = null;
constructor(auth) {
this.auth = auth;
}
get routes() {
if (this.auth.currentUser) {
return this.router.navigation;
}
return this.router.navigation.filter(r => !r.settings.auth);
}
}
Rather than iterating over router.navigation at this point, nav-bar.html will iterate over the routes property I declared above:
<ul class="nav navbar-nav">
<li repeat.for="row of routes" class="${row.isActive ? 'active' : ''}">
<a data-toggle="collapse" data-target="#skeleton-navigation-navbar-collapse.in" href.bind="row.href">${row.title}</a>
</li>
</ul>
Again, your mileage may vary, but I wanted to post this as I thought it was a fairly clean and painless solution to a common requirement.