Generate OAuth2 token programiticly , No lgoin needed in my case - c#

I use OAuth2 with asp.net WEB API.
In my case the users will login using only their phone numbers, and they will receive SMS with verification code.
The users confirm their phone number using the verification code with this method:
[AllowAnonymous]
[Route("ConfrimPhoneNumber")]
[HttpPost]
public async Task<IHttpActionResult> VerifyPhoneNumber(VerfyPhoneModel Model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Verify phone number.
//..
//..
//Get Application User
// I need to genereate and return token from here .
}
What i want is to generate access token and refresh token for this user.
thank you for your help in advance.

I'll assume you use the default SPA template in Visual Studio. In Startup.Auth.cs you have a static property:
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
If you want to create an OAuth2 token you can use this static reference to generate the token:
Startup.OAuthOptions.AccessTokenFormat.Protect(authenticationTicket);

I want to share what I've got to make this work i collect the answer from different other posts; you can find the link in the end of this answer.
If any one has a comment or idea please share it with us.
Generate Local access Token with refresh token After user register as Response.
As Peter Hedberg Answer; We need to make OAuthOptions Puplic and static in the startup class as :
public static OAuthAuthorizationServerOptions OAuthServerOptions { get; private set; }
Then i created helper class to generate local access token and refresh
public async Task<JObject> GenerateLocalAccessToken(ApplicationUser user)
{
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
//Create the ticket then the access token
var ticket = new AuthenticationTicket(oAuthIdentity, properties);
ticket.Properties.IssuedUtc = DateTime.UtcNow;
ticket.Properties.ExpiresUtc = DateTime.UtcNow.Add(Startup.OAuthServerOptions.AccessTokenExpireTimeSpan);
var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
//Create refresh token
Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext context =
new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext(
Request.GetOwinContext(),
Startup.OAuthOptions.AccessTokenFormat, ticket);
await Startup.OAuthOptions.RefreshTokenProvider.CreateAsync(context);
properties.Dictionary.Add("refresh_token", context.Token);
//create the Token Response
JObject tokenResponse = new JObject(
new JProperty("access_token", accessToken),
new JProperty("token_type", "bearer"),
new JProperty("expires_in", Startup.OAuthServerOptions.AccessTokenExpireTimeSpan.TotalSeconds.ToString()),
new JProperty("refresh_token", context.Token),
new JProperty("userName", user.UserName),
new JProperty(".issued", ticket.Properties.IssuedUtc.ToString()),
new JProperty(".expires", ticket.Properties.ExpiresUtc.ToString())
);
return tokenResponse;
}
There is a problem to use basic context.SerializeTicket in SimpleRefreshTokenProvider CreateAsync method. Message from Bit Of Technology
Seems in the ReceiveAsync method, the context.DeserializeTicket is not
returning an Authentication Ticket at all in the external login case.
When I look at the context.Ticket property after that call it’s null.
Comparing that to the local login flow, the DeserializeTicket method
sets the context.Ticket property to an AuthenticationTicket. So the
mystery now is how come the DeserializeTicket behaves differently in
the two flows. The protected ticket string in the database is created
in the same CreateAsync method, differing only in that I call that
method manually in the GenerateLocalAccessTokenResponse, vs. the Owin
middlware calling it normally… And neither SerializeTicket or
DeserializeTicket throw an error…
So, you need to use Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer to searizize and deserialize ticket. It will be look like this:
Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer serializer
= new Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer();
token.ProtectedTicket = System.Text.Encoding.Default.GetString(serializer.Serialize(context.Ticket));
instead of:
token.ProtectedTicket = context.SerializeTicket();
And for ReceiveAsync method:
Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer serializer = new Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer();
context.SetTicket(serializer.Deserialize(System.Text.Encoding.Default.GetBytes(refreshToken.ProtectedTicket)));
instead of:
context.DeserializeTicket(refreshToken.ProtectedTicket);
Please refer to this Qestion and this Answer
thank you lincx and Giraffe

Related

Auth token from dialog to bot class

I am running an OAuth Dialog that allows user to sign in. I am looking to get this Auth token from DialogsClass.cs to my Bot.Cs class file and use it to make Graph calls.
I have tried to save token as string in local file within my dialog class and then read it back in main bot class but this solution does not seems as a right way of doing it.
AuthDialog.cs in Waterfall step:
var tokenResponse = (TokenResponse)stepContext.Result;
Expected result. Transfer this token from Dialog class to MainBot.cs class and use as string to make Graph calls.
Are you using one waterfall step to get token with OAuthPrompt and then another step to call a different class (in which you do graph api calls)?
Why can't you just pass the token to the down stream class?
If there are other steps in the middle, there are multiple ways to resolve it:
Use WaterfallStepContext Values
Save to your own UserState
Microsoft suggests not to store token in the system but make a call to oAuth prompt
return await stepContext.BeginDialogAsync(nameof(OAuthPrompt), null, cancellationToken);
and get latest token whenever you have to call Graph API. Once you receive the token in var tokenResponse = (TokenResponse)stepContext.Result;
you can make a call to GraphClient class which will create the Graph API client using the token in Authorization attribute.
var client = new GraphClientHelper(tokenResponse.Token);
Graph Client implementation:
public GraphClientHelper(string token)
{
if (string.IsNullOrWhiteSpace(token))
{
throw new ArgumentNullException(nameof(token));
}
_token = token;
}
private GraphServiceClient GetAuthenticatedClient()
{
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
requestMessage =>
{
// Append the access token to the request.
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", _token);
// Get event times in the current time zone.
requestMessage.Headers.Add("Prefer", "outlook.timezone=\"" + TimeZoneInfo.Local.Id + "\"");
return Task.CompletedTask;
}));
return graphClient;
}
Once graph client is created you can make a call to the intended graph api:
await client.CreateMeeting(meetingDetails).ConfigureAwait(false);
Please refer this sample code:
Graph Sample

502.3 when calling AcquireTokenAsync in .NET Core 2.2

I'm encountering a problem. I am using Microsoft Graph to get the current logged in user via OnBehalfOfMsGraphAuthenticationProvider.cs as seen in the following solution.
This has been working flawlessly, but I have been doing some refactoring, and suddenly I get an error when trying to execute my authContext.AcquireTokenAsync() method.
HTTP Error 502.3 - Bad Gateway
The code in question looks like this:
public async Task AuthenticateRequestAsync(HttpRequestMessage request) {
var httpContext = _httpContextAccessor.HttpContext;
//Get the access token used to call this API
string token = await httpContext.GetTokenAsync("access_token");
//We are passing an *assertion* to Azure AD about the current user
//Here we specify that assertion's type, that is a JWT Bearer token
string assertionType = "urn:ietf:params:oauth:grant-type:jwt-bearer";
//User name is needed here only for ADAL, it is not passed to AAD
//ADAL uses it to find a token in the cache if available
var user = httpContext.User;
string userName =
user.FindFirst(ClaimTypes.Upn).Value ?? user.FindFirst(ClaimTypes.Email).Value;
var userAssertion = new UserAssertion(token, assertionType, userName);
//Construct the token cache
var cache = new DistributedTokenCache(user, _distributedCache,
_loggerFactory, _dataProtectionProvider);
var authContext = new AuthenticationContext(_configuration["AzureAd:Instance"] +
_configuration["AzureAd:TenantId"], true, cache);
var clientCredential = new ClientCredential(_configuration["AzureAd:ClientId"],
(string) _configuration["AzureAd:ClientSecret"]);
//Acquire access token
var result = await authContext.AcquireTokenAsync("https://graph.microsoft.com", clientCredential, userAssertion); //This is where it crashes
//Set the authentication header
request.Headers.Authorization = new AuthenticationHeaderValue(result.AccessTokenType, result.AccessToken);
}
I am calling it from my OrdersController:
// POST: api/Orders
[HttpPost]
public async Task<IActionResult> CreateAsync([FromBody] OrderDTO order) {
if (!ModelState.IsValid) {
return BadRequest(ModelState);
}
var graphUser = await this.graphApiService.GetUserProfileAsync();
The refactoring has consisted of dividing my solution into two class library projects and one web project - the latter has the controllers and the React app. GraphAPiClient and the provider are located in the Core library like this:
Screenshot of architecture
So, it turns out that the problem appeared when I upgraded the package Microsoft.IdentityModel.Clients.ActiveDirectory from v3.19.8 to v4.4.1. For some reason, no versions above v3.19.8 work with my code, causing it to crash when I try to make the call to https://graph.microsoft.com, but as soon as I downgraded the problem disappeared.
Try using AcquireToken instead of AcquireTokenAsync
azureAuthenticationContext.AcquireToken

Xamarin, Azure, customauth and passing parameters

I am in the process of rewritting our app using Xamarin.Forms with a C# backend and I'm trying to use customauth on login. I've got it working to a point but am struggling to pass back to the Xamarin app everything I want from the backend. I'm getting the token and user id but want a bit more.
The backend code on succesfull login seems relatively straightforward:
return Ok(GetLoginResult(body));
where GetLoginResult() is:
private object GetLoginResult(IUser body)
{
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, body.username)
};
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(
claims, signingKey, audience, issuer, TimeSpan.FromDays(30));
accounts account = db.accounts.Single(u => u.username.Equals(body.username));
return new LoginResult(account)
{
authenticationToken = token.RawData,
};
}
and the LoginResult class is
public class LoginResult
{
public LoginResult(accounts account)
{
Response = 200;
CustomerId = account.CustomerId;
Modules = account.Modules;
User = new LoginResultUser
{
userId = account.id,
UserName = account.UserName,
EmployeeId = account.EmployeeId
};
}
[JsonProperty(PropertyName = "Response")]
public int Response { get; set; }
etc
In the app, I'm calling the customauth as follows:
MobileServiceUser azureUser = await _client.LoginAsync("custom", JObject.FromObject(account));
The result has the token and the correct userid but how can I fill the result with the additional properties passed back by the backend? I've got the backend working and tested using postman and the results I get there are what I want but I've been unable to find out how to get it deserialized in the app.
As I known, for custom auth , MobileServiceClient.LoginAsync would invoke https://{your-app-name}.azurewebsites.net/.auth/login/custom. When using ILSPy you could find that this method would only retrieve the user.userId and authenticationToken from the response to construct the CurrentUser of your MobileServiceClient. Per my understanding, you could leverage MobileServiceClient.InvokeApiAsync to retrieve the additional user info after the user has logged in successfully. Additionally, you could try to follow this toturial for other possible approaches.
UPDATE
You could use InvokeApiAsync instead of LoginAsync to invoke the custom login endpoint directly, then retrieve the response and get the additional parameters as follows:
When logged successfully, I added a new property userName and response the client as follows:
For the client, I added a custom extension method for logging and retrieve the additional parameters as follows:
Here are the code snippet, you could refer to them:
MobileServiceLoginExtend.cs
public static class MobileServiceLoginExtend
{
public static async Task CustomLoginAsync(this MobileServiceClient client, LoginAccount account)
{
var jsonResponse = await client.InvokeApiAsync("/.auth/login/custom", JObject.FromObject(account), HttpMethod.Post, null);
//after successfully logined, construct the MobileServiceUser object with MobileServiceAuthenticationToken
client.CurrentUser = new MobileServiceUser(jsonResponse["user"]["userId"].ToString());
client.CurrentUser.MobileServiceAuthenticationToken = jsonResponse.Value<string>("authenticationToken");
//retrieve custom response parameters
string customUserName = jsonResponse["user"]["userName"].ToString();
}
}
Login processing
MobileServiceClient client = new MobileServiceClient("https://bruce-chen-002-staging.azurewebsites.net/");
var loginAccount = new LoginAccount()
{
username = "brucechen",
password = "123456"
};
await client.CustomLoginAsync(loginAccount);

How can I get ExternalIdentity after all the login process?

I am using MVC 5 and I can successfully login using Google.
I want to have access to the user external identity claims after the login process. I want in a view to access, for example, the claim "picture" from the user. However if I try to run this code it always return null. (except in the login process - auto generated code for mvc template)
Is there a way for me to have access to the external identity claims? (after the login process)
I found how the identity is created. Basically the ExternalSignInAsync makes an internal call to SignInAsync which makes a call to CreateUserIdentityAsync.
I found a class ApplicationSignInManager in the IdentityConfig file and then I changed the CreateUserIdentityAsync method to:
public override async Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var localIdentity = await user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
foreach (var item in externalIdentity.Claims)
{
if (!localIdentity.HasClaim(o => o.Type == item.Type))
localIdentity.AddClaim(item);
}
return localIdentity;
}
So every time I sign in I am going to have my claims + external claims in the loggedin user. From a view I can call:
#HttpContext.Current.GetOwinContext()
.Authentication.User.FindFirst("urn:google:picture").Value
You need to store the auth token and then use that to query the login provider's API for the information you need. Storing it is easy enough:
Startup.Auth.cs
const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
...
var googlePlusOptions = new GoogleOAuth2AuthenticationOptions
{
ClientId = "yourclientid",
ClientSecret = "yourclientsecret",
Provider = new GoogleOAuth2AuthenticationProvider
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:googleplus:access_token", context.AccessToken, XmlSchemaString, "Google"));
return Task.FromResult(0);
}
}
};
app.UseGoogleAuthentication(googlePlusOptions);
Then, after you create the new user in ExternalLoginCallback or ExternalLoginConfirm:
await SaveAccessToken(user, identity);
With the following definition for SaveAccessToken (just put it with the other helper methods in the controller):
private async Task SaveAccessToken(User user, ClaimsIdentity identity)
{
var userclaims = await UserManager.GetClaimsAsync(user.Id);
foreach (var at in (
from claims in identity.Claims
where claims.Type.EndsWith("access_token")
select new Claim(claims.Type, claims.Value, claims.ValueType, claims.Issuer)))
{
if (!userclaims.Contains(at))
{
await UserManager.AddClaimAsync(user.Id, at);
}
}
}
Now, you'll have the access token to use later whenever you need it. So, for Google, to get the profile photo, you'd just send a request to https://www.googleapis.com/oauth2/v3/userinfo?access_token=[token], where [token] is the value of the claim you saved.

Getting OAuth2 refresh token

I'm trying to use Google's Calendar API to demo out an OAuth2 integration that we'll need to do with another third party. I'm using the DotNetOpenAuth library, and I've been able to get the initial redirect to Google for the Allow / Deny prompt and get the authorization code back.
I now need to get the access token and refresh token, but I only seem to get an access token back, refresh token is null.
This is my controller action method where Google redirects back to after the user Accepts or Denies:
public ActionResult ProcessResponse(string state, string code, string error)
{
var oAuthClient =
new WebServerClient(
new AuthorizationServerDescription
{
TokenEndpoint = new Uri("https://accounts.google.com/o/oauth2/token"),
AuthorizationEndpoint = new Uri("https://accounts.google.com/o/oauth2/auth"),
ProtocolVersion = ProtocolVersion.V20
},
_applicationId,
_secret)
{
AuthorizationTracker = new TokenManager()
};
var authState = oAuthClient.ProcessUserAuthorization();
var accessToken = authState.AccessToken;
var refreshToken = authState.RefreshToken;
return View(new[] { accessToken, refreshToken });
}
Any ideas?
EDIT:
To get the authorization code, I setup the oAuthClient identically to what I did above, and use this method:
oAuthClient.RequestUserAuthorization(new[] { "https://www.googleapis.com/auth/calendar" }, returnUrl);
I had a similar problem, and solved mine by hand-coding the HttpRequest and HttpResponse handling. See code at: https://stackoverflow.com/a/11361759/29156

Categories

Resources