Search code examples
c#asp.net-mvcasp.net-mvc-5asp.net-identityrestsharp

Is it possible use AuthorizeAttribute with a external api call?


i'm working with asp mvc 5, and i have to schedule some tasks, so i want to create a simple methods to be called from a simple console program C# and schedule them with Windows Task Scheduler.

The think is, i'm using Identity with Authorize attribute to manage the user permissions.

For example, i have the next method:

[Authorize(Roles="Admin")]
public async Task<JsonResult> CriticalTask(string someParam)
{
 //procesing data
 return null;
}

The think, is:

I dont know how can i do the login to pass the validation from the Authorize(Roles="Admin")

I try creating a simple method to login before it, but that doesn't work

I'm trying some like this

        const string URL = "http://localhost:53665/";
        RestClient mClient = new RestClient(URL);
        const string parameterUserName = "userName";
        const string parameterPassword = "password";
        const string ruta = "Usuarios/ApiLogin";

        var request = new RestRequest(ruta);

        request.AddParameter(parameterUserName, "userName");
        request.AddParameter(parameterPassword, "password");

        //Method to login
        var result2 = mClient.Execute(request);
        Console.WriteLine($"Login\n{result2.Content}");

        //Method that needs Admin permissions
        request = new RestRequest("Usuarios/Test");
        var result3 = mClient.Execute(request);

        Console.WriteLine($"Test\n{result3.Content}");

is that possible only with Authorize attribute? or i need to implement some token method to authorize this calls?

Thanks!


Solution

  • The easiest solution would be to use BasicAuth - you pass Credentials in headers for each request, and each request is validated separately - search for MVC Basic auth fr sample setup. I's the easiest form - but also very insecure as you pass your credentials in each call in almost plain text (it is only base64 of your credentials)

    I'd suggest you to use Identity Server 4 to authorize your client using bearer token.

    this way before first call you request token from server and then pass it to following requests and use this to authorize your api calls.

    see following tutorial about setup. http://docs.identityserver.io/en/aspnetcore1/quickstarts/6_aspnet_identity.html

    in following url you can see example of in memory users, but also token requests.it's easy but obsolete https://neelbhatt.com/2018/03/04/step-by-step-setup-for-the-auth-server-and-the-client-identityserver4-with-net-core-part-ii/

    you can also use some sort of following code to obtain token in less obsolete way:

    using (var httpClient = new HttpClient()) {
        var discovery = await _httpClient.GetDiscoveryDocumentAsync(_configuration["ApiBaseAddress"]);
    
        if (discovery.IsError)
        {
              return false;
        }
    
        request.Address = discovery.TokenEndpoint;
        request.ClientId = _configuration["AuthClientName"];
        request.ClientSecret = _configuration["AuthClientSecret"];
    
        var request = new PasswordTokenRequest
               {
                    UserName = "yourUserName",
                    Password = "yourPassword"
                };
        var token = await _httpClient.RequestPasswordTokenAsync(request);
    }
    

    in token.AccessToken you have your access token - the one needed to be sent to call api. you also have your refresh token in token.RefreshToken - it will be useful later

    then to send call simply add bearer token to your HttpRequestMessage and it's done

    var _httpClient =  new HttpClient();
    //Configure your http client here
    var req= new HttpRequestMessage("http request method", "request uri");
    req.SetBearerToken("your access token goes here);
    var result = await _httpClient.SendAsync(req);
    

    keep in mind that after you receive permission denied it's better to refresh token than obtain another one (you don't need to send your credentials). You use your refresh token i mentioned earlier.

    The code for refreshing token is very similar to obtaining token by login/password. Just instead of PasswordTokenRequest class use following class:

    var request = new RefreshTokenRequest
    {
        RefreshToken = _refreshToken
    };
    

    and instead httpClient.RequestPasswordTokenAsync(request) use httpClient.RequestRefreshTokenAsync(request). rest of code may remain similar.