public async Task <IActionResult> Exchange(string device_code, OpenIdConnectRequest request, CancellationToken cancellationToken) { Debug.Assert(request.IsTokenRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); if (request.IsDeviceCodeGrantType()) { if (request.ClientId == null) { return(BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidClient, ErrorDescription = "Missing required parameter client_id." })); } // exchange device code for tokens var application = await _applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted); if (application == null) { return(BadRequest(new ErrorViewModel { Error = OpenIdConnectConstants.Errors.InvalidClient, ErrorDescription = "Details concerning the calling client application cannot be found in the database" })); } var code = await _deviceCodeManager.FindByDeviceCodeAsync(device_code); if (code == null) { return(BadRequest(new ErrorViewModel { Error = OpenIdConnectConstants.Errors.AccessDenied, ErrorDescription = "Access denied" // todo: use canonical descriptions message for these errors })); } if (code.AuthorizedOn == default(DateTimeOffset)) { if (code.LastPolledAt == default(DateTimeOffset)) { code.LastPolledAt = DateTimeOffset.Now; await _deviceCodeManager.UpdateLastPolledAt(code); } else { var interval = DateTimeOffset.Now - code.LastPolledAt; if (interval.TotalMilliseconds < _deviceCodeOptions.Interval * 950) { return(BadRequest(new ErrorViewModel { Error = DeviceCodeFlowConstants.Errors.SlowDown, ErrorDescription = "Slow down" })); } else { code.LastPolledAt = DateTimeOffset.Now; await _deviceCodeManager.UpdateLastPolledAt(code); } } return(BadRequest(new ErrorViewModel { Error = DeviceCodeFlowConstants.Errors.DeviceCodeAuthorizationPending, ErrorDescription = "Device code authorization pending" })); } var user = await _userManager.FindByIdAsync(code.Subject); if (user == null) { return(BadRequest(new ErrorViewModel { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "An internal error has occurred" })); } await _deviceCodeManager.Consume(code); // Ensure the user is still allowed to sign in. if (!await _signInManager.CanSignInAsync(user)) { return(BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "The user is no longer allowed to sign in." })); } var ticket = await CreateTicketAsync(new OpenIdConnectRequest { Scope = string.Join(" ", code.Scopes), ClientId = request.ClientId, ClientSecret = request.ClientSecret, GrantType = request.GrantType }, user); return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); } else if (request.IsPasswordGrantType()) { // not implemented } else if (request.IsAuthorizationCodeGrantType()) { // Retrieve the claims principal stored in the authorization code. var info = await HttpContext.Authentication.GetAuthenticateInfoAsync( OpenIdConnectServerDefaults.AuthenticationScheme); // Retrieve the user profile corresponding to the authorization code. var user = await _userManager.GetUserAsync(info.Principal); if (user == null) { return(BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "The authorization code is no longer valid." })); } // Ensure the user is still allowed to sign in. if (!await _signInManager.CanSignInAsync(user)) { return(BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "The user is no longer allowed to sign in." })); } // Create a new authentication ticket, but reuse the properties stored // in the authorization code, including the scopes originally granted. var ticket = await CreateTicketAsync(request, user, info.Properties); return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); } return(BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.UnsupportedGrantType, ErrorDescription = "The specified grant type is not supported." })); }