Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: How can i get a token from a controller? #47

Closed
Gillardo opened this issue Jan 13, 2016 · 6 comments
Closed

Question: How can i get a token from a controller? #47

Gillardo opened this issue Jan 13, 2016 · 6 comments
Labels

Comments

@Gillardo
Copy link

Creating a webApi using asp.net 5, but i do not want to use cookies, and i have implemented googleAuthentication. This doesnt work using the standard SignInManger, as for some reason i get an error message saying No authentication handler is configured to authenticate for the scheme: Microsoft.AspNet.Identity.External, as i have logged on StackOverflow http://stackoverflow.com/questions/34763335/no-authentication-handler-is-configured-to-authenticate-for-the-scheme-microsof

To get round this issue, i have implemented my own code to connect to google, get the accessToken and then get the user info from google. Now i can add the user and login to my applicationContext, all fine. Now i need to return a JSON Web Token. How can i do this from my AuthController?? I also need to do the same thing when a user logs in using an email and password.

Here is my code, maybe i am missing something??

startup.cs

public void ConfigureServices(IServiceCollection services)
{
    // Add Authorization so we can use the [Authorize] tag 
    services.AddAuthorization(options =>
    {
        options.DefaultPolicy = new AuthorizationPolicyBuilder().
                AddAuthenticationSchemes(OAuthBearerAuthenticationDefaults.AuthenticationScheme).
                RequireAuthenticatedUser().
                Build();
    });

    services.AddEntityFramework()
        .AddSqlServer()
        .AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(_configuration["Data:SecurityConnection:ConnectionString"]));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders()
        .AddOpenIddict(); // Add the OpenIddict services after registering the Identity services.
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    // use jwt bearer authentication
    app.UseJwtBearerAuthentication(options =>
    {
        options.AutomaticAuthenticate = true;
        options.AutomaticChallenge = true;
        options.RequireHttpsMetadata = false;
        options.Audience = "http://localhost:5000/";
        options.Authority = "http://localhost:5000/";
    });

    // Add all the external providers you need before registering OpenIddict:
    app.UseGoogleAuthentication(options =>
    {
        options.ClientId = "XXX";
        options.ClientSecret = "XXX";
    });
    //app.UseFacebookAuthentication();

    app.UseOpenIddict();

    // Enable all static file middleware
    app.UseStaticFiles();

    // Enable Mvc for view controller, and 
    // default all routes to the Home controller
    app.UseMvc(options =>
    {
        options.MapRoute(
            name: "default",
            template: "{*url}",
            defaults: new { controller = "Home", action = "Index" });
    });
}

AuthController.cs

public class AuthController : Controller
{
    private UserManager<ApplicationUser> _userManager;
    private GoogleApiService _googleApiService;
    private ApplicationDbContext _context;
    private OpenIddictProvider<ApplicationUser, OpenIddict.Models.Application> _provider;

    public AuthController(
        UserManager<ApplicationUser> userManager,
        GoogleApiService googleApiService,
        ApplicationDbContext applicationDbContext,
        OpenIddictProvider<ApplicationUser, OpenIddict.Models.Application> provider)
    {
        _userManager = userManager;
        _googleApiService = googleApiService;
        _context = applicationDbContext;
        _provider = provider;
    }

    /// <summary>
    /// POST: api/v1/auth/login
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginModel model)
    {
        // validate user HERE

               // How can i return a JSON web token here?
    }

    [HttpPost("google")]
    public async Task<IActionResult> GoogleAsync([FromBody] ExternalLoginModel model)
    {
        // get token from google, so we can get the users details
        var token = await _googleApiService.GetTokenAsync(model.Code, model.RedirectUri);

        // get basic user info
        var userInfo = await _googleApiService.GetUserInfoAsync(token);

        // get user from context
        var login = await _context.UserLogins
            .Where(e => e.LoginProvider == "google" && e.ProviderKey == userInfo.Id)
            .FirstOrDefaultAsync();

        // if user doesnt exist, create one
        if (login == null)
        {
            var applicationUser = new ApplicationUser { UserName = userInfo.Email, Email = userInfo.Email };
            var result = await _userManager.CreateAsync(applicationUser);

            // check if user added
            if (!result.Succeeded)
                throw new ApplicationException("User could not be created");

            // if the user was created successfully, add external login
            result = await _userManager.AddLoginAsync(applicationUser, new UserLoginInfo("google", userInfo.Id, userInfo.Name));

            if (!result.Succeeded)
                throw new ApplicationException("User could not be created");
        }

               // How can i return a JSON web token here?
        return Ok();
    }
}
@kevinchalet
Copy link
Member

Now i need to return a JSON Web Token. How can i do this from my AuthController?? I also need to do the same thing when a user logs in using an email and password.

This is not something we support (at all): aspnet-contrib/AspNet.Security.OpenIdConnect.Server#194 (comment)

Though popularized by well-known libraries like Satellizer, the flow you're trying to implement - that consists in starting the authorization process from your JS app and making the token request server-side - is not really standard and thus, not supported by OpenIddict (not to mention that you have to be extremely careful to avoid session fixation or XSRF attacks).

Here's the recommended approach:

  • Let your ASP.NET 5 app (not the JS side) handle the external authentication process, using the appropriate social providers (app.UseFacebookAuthentication()/app.UseGoogleAuthentication()).
  • Register your OpenIddict server in the Satellizer options, but not the external providers:
$authProvider.oauth2({
    name: 'openiddict',
    url: '/auth/openiddict',
    clientId: '[the client_id you assigned to your JS app in Startup.cs]',
    redirectUri: window.location.origin,
    authorizationEndpoint: '[your ASP.NET 5 base address]/connect/authorize',
});
  • Optionally, customize your login page (by default, in AccountController) to render a popup-friendly form, where the social providers are listed.

I'd love to add an AngularJS sample for OpenIddict demonstrating the best practices. Would you like to work with me on a sample using Satellizer?

@Gillardo
Copy link
Author

So the accountController needs a server side view, like the ones in the example? Does this also allow you to "not" use cookies? Just want to get this work asap, but the right way as well.

Happy to help on a sample, that would really help me. What would you like me to do? I can strip my current application, to a very basic with angular and satellizer if you want?

@kevinchalet
Copy link
Member

So the accountController needs a server side view, like the ones in the example? Does this also allow you to "not" use cookies? Just want to get this work asap, but the right way as well.

True, you'd need server side views to render the login page (and you'd have to use cookies, at least during the login process).

Happy to help on a sample, that would really help me. What would you like me to do? I can strip my current application, to a very basic with angular and satellizer if you want?

Yeah, having a basic Angular/Satellizer app would be nice 👍

@Gillardo
Copy link
Author

Working on one now 👍

@Gillardo
Copy link
Author

This is harder than i thought, i have created a basic app, but because the angular app is on the Index page, you cant just open the server side Accounts page, as this will lose the angular app, and thus Satellizer will not work. I am figuring out something else though, so will keep you posted

@Gillardo
Copy link
Author

Right, i am created a basic angular app (using the Mvc.Server as a template) and i can open a window, login and callback to the angular app. How can i get the accessToken for the logged in user??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants