How to authorize user with taken token from IdentityServer? - .net-core

I have created an IdentityServer project ( B ) with this configuration :
new Client
{
ClientId = "ro.client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "api1" }
}
And I have project B with it's custom login page, When the user enter his/her user & password, I send this info to project B's backend, and through this code I can get the token from identity server - project A:
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync(_configuration["App:Authority"]);
if (disco.IsError)
{
throw new System.Exception("get info from identity server is failed.");
}
var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "ro.client",
ClientSecret = "secret",
UserName = "bob",
Password = "Pass123$",
Scope = "api1"
});
var accessToken = tokenResponse.AccessToken;
Now what I want to achieve is that somehow I make this user authorized and save the token in cookie, So when this user call another action with authorize attribute, does not redirect him/her to login page.
How can I achieve this ?

Related

How to create refresh_token in identity Server c#? Thank u

i have access_token from CreateResponseAsync. Now i want create refresh_token, pls help me.
// validate request
var result = await _validator.ValidateAsync(parameters, userResult.user);
if (result.IsError)
{
return BadRequest("Request validation failed");
}
var request = result.ValidatedRequest;
var authorizeResponse = await _generator.CreateResponseAsync(request);
// create refresh token by IRefreshTokenService
var response = new LoginResponse()
{
code = authorizeResponse.Code,
id_token = authorizeResponse.IdentityToken,
access_token = authorizeResponse.AccessToken,
};

Redirect after sign-out through OpenID Connect not working

I am building a Blazor Server ASP.NET Core application with cookie based authentication through a OpenID Connect (OIDC) provider. On sign-out I want to be redirected to localhost URI: https://localhost:44378/signout-oidc. This is the path registrered as Post Logout Redirect URI at the OIDC Provider.
When I sign-out I am sent through the sign-out flow at the Connect provider, their log states "End session request validation success", but I end up on the URI: https://localhost:44378/signout-oidc?state=CfDJ8LdQ[...] which is a blank page. If I try to access https://localhost:44378/signout-oidc while I am logged in I get Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler: Error: The remote signout request was ignored because the 'sid' parameter was missing, which may indicate an unsolicited logout.
I have tried a myriad of different combinations of overriding the SignedOutRedirectUri, SignedOutCallbackPath, RemoteSignOutPath, etc., as well as redirecting to other pages - all to no avail. I am running out of ideas and would appreciate all kinds of input.
I have added the code for the configuration of OpenID authentication:
services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
opt.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
}).AddCookie("Cookies", options =>
{
options.Cookie.SameSite = SameSiteMode.None;
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = configuration.GetSection("AuthorizationStrings")["Authority"];
options.ClientId = configuration.GetSection("AuthorizationStrings")["ClientId"];
options.ClientSecret = configuration.GetSection("AuthorizationStrings")["ClientSecret"];
options.ResponseType = "code";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.UseTokenLifetime = false;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name" };
options.SignedOutCallbackPath = "/signout-oidc";
Code for the sign-out flow which is initiated through a sign-out button. The formatting of the Redirect URI is how the OIDC provider expects it (the state parameter is optional so I have left it out):
public async Task OnGetAsync()
{
var ac = await HttpContext.GetTokenAsync("access_token");
String uri = String.Format("[CONNECT PROVIDER URI]/endsession?id_token_hint={0}&post_logout_redirect_uri=https://localhost:44378/signout-oidc", ac);
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
var prop = new AuthenticationProperties
{
RedirectUri = uri
};
await HttpContext.SignOutAsync("oidc", prop);
}
You might want to set the LogoutPath, as it must match the URL of the "Logout" link; if they do match, then the RedirectUri will be accepted.
}).AddCookie(options =>
{
options.LogoutPath = "/User/Logout";
...
});
See this answer here for more details.
This is my Logout code for /User/Logout
/// <summary>
/// Do the logout
/// </summary>
/// <returns></returns>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
//Important, this method should never return anything.
}

JWT tokken in Web API project

I had implemented a JWT token in my web API project with roles management.its working fine. Authorization attributes also working well. Role Management also implemented with the JWT.
Here is the Controller side Code.
public Object Authentication(string objuser, string password)
{
var Login = UserLogin.DoLogin(objuser, password);
if (Login!=null)
{
if (Login.Email== "Sucess")
{
string UserRole = GetUserRole(Login.UserType);**//Admin,Buyer,Seller**
string issuer = ConfigurationManager.AppSettings["Url"].ToString();
var key = ConfigurationManager.AppSettings["AuthTokenKey"].ToString();
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var permClaims = new List<Claim>();
permClaims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));
permClaims.Add(new Claim("valid", "1"));
permClaims.Add(new Claim("userid", Login.User_ID.ToString()));
permClaims.Add(new Claim("Email", Login.Email));
permClaims.Add(new Claim(ClaimTypes.Role, UserRole));
var token = new JwtSecurityToken(issuer,
issuer,
permClaims,
expires: DateTime.Now.AddDays(1),
signingCredentials: credentials);
var jwt_token = new JwtSecurityTokenHandler().WriteToken(token);
Login.Token = jwt_token;
return Login;
}
}
return Login;
}
The startup file code is here
public void Configuration(IAppBuilder app)
{
//createRolesandUsers();
string Url=ConfigurationManager.AppSettings["Url"].ToString();
string key = ConfigurationManager.AppSettings["AuthTokenKey"].ToString();
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Url, //some string, normally web url,
ValidAudience = Url,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key))
}
});
}
Ajax side code
I am sending this token from ajax like this way:
$.ajax({
url: pathtoBuyerDetails,
type: "Get",
**headers: { Authorization: 'Bearer ' + sessionStorage.getItem('AuthorizeToken')},**
data: { Time: Time, UID: UID },
success: function (values) {something}
The new requirement from my client is that the token should be validated with UserID. i.e
first Buyer login and his UserID=2 and TokenID="eyJqdGkiOiJkNTEwMzEwMC1jZDI3LTQxY2QtOTFmZS1iZGFjOTY5ZTMwOTUiLCJ2YWxpZCI6IjEiLCJleHAiOjE2MDExMjI4NTksImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTUyNTMvIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo1NTI1My8ifQ"
Second Buyer login and his UserID=3 and TokenID="JqdGkiOiJkNTEwMzEwMC1jZDI3LTQxY2QtOTFmZS1iZGFjOTY5ZTMwOTUiLCJ2YWxpZCI6IjEiLCJleHAiOjE2MDExMjI4NTksImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTUyNTMvIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo1NTI1My8ifQ.zEazMtGdWSjMOj4EqrCM3BX"
Required Solution
if 2nd Buyer changes his id from 3 to 2 in session-storage and hit the URL he should not access the data of First Buyer because its token id does not match the first buyer token id.
In short, we need to ensure that a buyer cannot access another buyer’s data. This means we need to check the JWT to ensure that the user ID we passed as the parameter is the same as the owner of the JWT.

How to use refresh token to get a new access token from identity server 4 with Xamarin.Forms client

How to use refresh_token to obtain a new access token from identity server in Xamarin.Forms client?
I followed tutorial https://sinclairinat0r.com/2018/12/09/secure-data-access-with-identityserver4-and-xamarin-forms and created xamarin forms mobile app with authentication on IS4. I set an access token lifetime to few minutes. After access token expires, as excepted, applciation is no more able to access authorized endpoints. I have an refresh_token but i dont't know how to use it to obtain a new access token from identity server.
Client specified in is4 configuration:
new Client()
{
ClientId = "xamarin-client",
ClientName = "Xamarin client",
AllowedGrantTypes = { "authorization_code" },
AllowedScopes = {"openid", "profile", "values-api" },
AllowAccessTokensViaBrowser = true,
AllowOfflineAccess = true,
AlwaysIncludeUserClaimsInIdToken = true,
RequirePkce = true,
RequireClientSecret = false,
RedirectUris = { "https://iglooidentityserver.azurewebsites.net/grants" },
AccessTokenLifetime = 180,
}
Authenticator i've used:
var oAuth = new OAuth2AuthenticatorEx(
"xamarin-client",
"offline_access values-api",
new Uri("https://iglooidentityserver.azurewebsites.net/connect/authorize"),
new Uri("https://iglooidentityserver.azurewebsites.net/grants"))
{
AccessTokenUrl = new Uri("https://iglooidentityserver.azurewebsites.net/connect/token"),
ShouldEncounterOnPageLoading = false,
};
var presenter = new OAuthLoginPresenter();
presenter.Completed += Presenter_Completed;
presenter.Login(oAuth);
I handled this problem in an old project as follows, hope this helps you.
public async Task<string> GetAccessToken()
{
if ((_authService.AuthAccessTokenExpireIn - DateTime.Now).TotalMinutes < 10) {
var authResponse = await GetRefreshTokenAsync(_authService.AuthRefreshToken);
_authService.AuthAccessToken = authResponse.AccessToken;
_authService.AuthRefreshToken = authResponse.RefreshToken;
_authService.AuthAccessTokenExpireIn = authResponse.ExpiresIn;
}
return _authService.AuthAccessToken;
}
public async Task<UserToken> GetRefreshTokenAsync(string currentRefreshToken)
{
string data = string.Format("grant_type=refresh_token&client_id={0}&client_secret={1}&refresh_token={2}", GlobalSetting.Instance.ClientId, GlobalSetting.Instance.ClientSecret, refreshToken);
var token = await PostAsync<UserToken>(_httpClient,
GlobalSetting.Instance.TokenEndpoint,
data);
return token;
}
public async Task<UserToken> PostAsync<UserToken>(HttpClient httpClient, string uri, object data)
{
var content = new StringContent(JsonConvert.SerializeObject(data));
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
HttpResponseMessage response = await httpClient.PostAsync(uri, content);
await HandleResponse(response);
string serialized = await response.Content.ReadAsStringAsync();
UserToken result = await Task.Run(() => JsonConvert.DeserializeObject<UserToken>(serialized, _serializerSettings));
return result;
}

Login from Universal App to Web Api using Live Id

I'm trying to implement following functionality:
User signs in into Live Id account from Windows Phone 8.1 (or Universal) app.
App accesses Web Api that I develop with ASP.NET Web Api 2
In this Web Api I need to authenticate the user.
Later, I want to authenticate same user in web app
Here is what I'm doing, and it doesn't work.
In my Windows Phone App:
var authClient = new LiveAuthClient("http://myservice.cloudapp.net");
LiveLoginResult result = await authClient.LoginAsync(new string[] { "wl.signin" });
if (result.Status == LiveConnectSessionStatus.Connected)
{
connected = true;
var identity = await ConnectToApi(result.Session.AuthenticationToken);
Debug.WriteLine(identity);
}
And then
private async Task<string> ConnectToApi(string token)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://myservice.cloudapp.net/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// HTTP GET
HttpResponseMessage response = await client.GetAsync("api/values");
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
return result;
}
else
return response.ReasonPhrase;
}
}
And then in my web api I have following
public void ConfigureAuth(IAppBuilder app)
{
app.UseMicrosoftAccountAuthentication(
clientId: "my client id",
clientSecret: "my secret");
}
I registered http://myservice.cloudapp.net as redirect url.
The problem is authentication doesn't work, web api actions do not recognize the user.
I got it totally wrong. First, I actually need to use app.UseJwtBearerAuthentication method. The example was found here http://code.lawrab.com/2014/01/securing-webapi-with-live-id.html. But when I tried, I got this error in the output
IDX10500: Signature validation failed. Unable to resolve SecurityKeyIdentifier: 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 1,
Clause[0] = System.IdentityModel.Tokens.NamedKeySecurityKeyIdentifierClause
)
This one took me a while to figure out, until I found this post: JwtSecurityTokenHandler 4.0.0 Breaking Changes?
Putting these things together, I got the solution that seems to work now in my testing environment:
public void ConfigureAuth(IAppBuilder app)
{
var sha256 = new SHA256Managed();
var sKey = "<Secret key>" + "JWTSig";
var secretBytes = new UTF8Encoding(true, true).GetBytes(sKey);
var signingKey = sha256.ComputeHash(secretBytes);
var securityKeyProvider = new SymmetricKeyIssuerSecurityTokenProvider("urn:windows:liveid", signingKey);
var securityKey = securityKeyProvider.SecurityTokens.First().SecurityKeys.First();
var jwtOptions = new JwtBearerAuthenticationOptions()
{
//AllowedAudiences = new[] { "<url>" },
//IssuerSecurityTokenProviders = new[]
//{
// new SymmetricKeyIssuerSecurityTokenProvider("urn:windows:liveid",signingKey)
//},
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters()
{
IssuerSigningKeyResolver = (token, securityToken, keyIdentifier, validationParameters) =>
{
return securityKey;
},
ValidAudience = "<url>",
ValidIssuer = securityKeyProvider.Issuer
}
};
app.UseJwtBearerAuthentication(jwtOptions);
}
For anybody looking to do this from JavaScript I managed to get this working by following steps from this blog. You can find the audience by putting your token through jwt.io
https://blog.dirk-eisenberg.de/2014/08/30/validate-authentication_token-from-microsoft-liveid-with-node-express-jwt/
const validateLiveJWT = (token) => {
const secret = '<<SECRET>>';
const sha256 = crypto.createHash('sha256');
sha256.update(secret + 'JWTSig', 'utf8');
const secretBase64 = sha256.digest('base64');
const secret = new Buffer(secretBase64, 'base64');
const options = {
audience: '<<AUDIENCE>>',
issuer: 'urn:windows:liveid',
};
return new Promise((resolve) => {
jwt.verify(token, secret, options, (err: any, claims: any) => {
if (err) {
resolve(undefined);
} else {
resolve(claims);
}
});
});
}

Resources