I'm building a protected api for a web application.
for each web service call client sends an access token.
when call for a resource depending on the access token it returns different responses.
ex:- call to /employees will return accessible employees only. accessibility will be defined for each access token.
my question is how it's possible to cache the response if it's returned different things depend on the access token.
is the access token part of the request which is considered in caching?
can the API be REST if it's not cacheable?
is partial access to resource allowed in REST?
#DamithK please clear what you want to do i am not getting it.."when call for a resource depending on the access token it returns different responses.
But as much i understand is that you want to authenticate your each api call.
If you are using RestClient for call api you can do it in following way.To call Api
var client = new RestClient(Serviceurl);
var request = new RestRequest("/Apimethod/{Inputs}?oauth_consumer_key=1ece74e1ca9e4befbb1b64daba7c4a24", Method.GET);
IRestResponse response = client.Execute(request);
In your service
public static class Authentication
{
public static bool AuthenticateRequest(IncomingWebRequestContext context)
{
bool Authenticated = false;
try
{
NameValueCollection param = context.UriTemplateMatch.QueryParameters;
if (param != null && param["oauth_consumer_key"] != null)
{
string consumerSecretKey = "1ece74e1ca9e4befbb1b64daba7c4a24";
Authenticated = param["oauth_consumer_key"] == consumerSecretKey;
}
else
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
}
}
catch (Exception)
{
}
return Authenticated;
}
}
Validate each request in called method using
Authentication.AuthenticateRequest(WebOperationContext.Current.IncomingRequest)
All this is c# code
Related
I have an application where the backend is an asp.net web api and the front-end is a Blazor server side. Both projects are using net6.0.
I have implemented jwt token authentication, so users can register and login from the front-end.
My problem is that if the user refreshes a page, he automatically gets logged out. My understanding is that this can be solved using refresh token (I'm not sure if this understanding is correct).
I have tried to follow this guide: Refresh Token with Blazor WebAssembly and ASP.NET Core Web API
However since I'm using Blazor server side I cannot intercept HTTP Requests using the approach in the article.
My question is: in my Blazor server side application how can I prevent users automatically getting logged out due to page refresh and how can I intercept the http request?
UPDATE: Notice I already have everything working in regards to token and authentication between the back and frontend. The part that I'm missing is inside the blazor server side application in the program.cs file. I basically want to intercept all http request and call a method.
In program.cs I have:
builder.Services.AddScoped<IRefreshTokenService, RefreshTokenService>();
I want RefreshTokenService to be called on every http request. I have tried creating a middleware (which calls the RefreshTokenService), inside the program.cs like:
app.UseMyMiddleware();
But this only get called once.
Here's a very simplified version of an API client I'm using in my app that's also split into an ASP.NET Core API backend and a Blazor Server frontend.
The way it works is that the accessToken gets retreived from local storage and added as an authentication header to the HttpRequestMessage in my API client before each API call.
MyApiClient.cs
public class MyApiClient
{
private readonly IHttpClientFactory _clientFactory;
private readonly IMyApiTokenProvider _myApiTokenProvider;
public MyApiClient(IHttpClientFactory clientFactory, IMyApiTokenProvider myApiTokenProvider)
{
_clientFactory = clientFactory;
_myApiTokenProvider = myApiTokenProvider;
}
public async Task<ApiResponse<CustomerListResponse>> GetCustomersAsync()
{
//create HttpClient
var client = _clientFactory.CreateClient("MyApiHttpClient");
//create HttpRequest
var request = CreateRequest(HttpMethod.Get, "/getCustomers");
//call the API
var response = await client.SendAsync(request);
//if Unauthorized, refresh access token and retry
if(response.StatusCode == HttpStatusCode.Unauthorized)
{
var refreshResult = await RefreshAccessToken(client);
if (refreshResult.IsSuccess)
{
//save new token
await _backendTokenProvider.SetAccessToken(refreshResult.NewAccessToken);
//create request again, with new access token
var retryRequest = await CreateRequest(HttpMethod.Get, "/getCustomers");
//retry
response = await client.SendAsync(retryRequest);
}
else
{
//refresh token request failed
return ApiResponse<CustomerListResponse>.Error("Token invalid");
}
}
//parse response
var customers = await response.Content.ReadFromJsonAsync<ApiResponse<CustomerListResponse>>();
return customers;
}
private HttpRequestMessage CreateRequest<TRequest>(string command, HttpMethod method, TRequest requestModel = null) where TRequest : class
{
//create HttpRequest
var request = new HttpRequestMessage(method, command);
//add body if not empty
if (requestModel is not null)
{
request.Content = JsonContent.Create(requestModel);
}
//set the Auth header to the Access Token value taken from Local Storage
var accessToken = await _myApiTokenProvider.GetAccessToken();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return request;
}
private async Task<ApiResponse<RefreshTokenResponse>> RefreshAccessToken(HttpClient client)
{
var refreshToken = await _backendTokenProvider.GetRefreshToken();
if (refreshToken is null)
{
return ApiResponse<RefreshTokenResponse>.Error("Refresh token is null, cannot refresh access token");
}
var refreshRequest = CreateRequest(HttpMethod.Post, "/refreshToken", new RefreshTokenRequest(refreshToken));
var refreshResponse = await client.SendAsync(refreshRequest);
var refreshResult = await response.Content.ReadFromJsonAsync<ApiResponse<RefreshTokenResponse>>();
return refreshResult;
}
}
MyApiTokenProvider.cs
public class MyApiTokenProvider : IMyApiTokenProvider
{
private readonly ProtectedLocalStorage _protectedLocalStorage;
public MyApiTokenProvider(ProtectedLocalStorage protectedLocalStorage)
{
_protectedLocalStorage = protectedLocalStorage;
}
public async Task<string> GetAccessToken()
{
var result = await _protectedLocalStorage.GetAsync<string>("accessToken");
return result.Success ? result.Value : null;
}
public async Task<string> GetRefreshToken()
{
var result = await _protectedLocalStorage.GetAsync<string>("refreshToken");
return result.Success ? result.Value : null;
}
public async Task SetAccessToken(string newAccessToken)
{
await _protectedLocalStorage.SetAsync("accessToken", newAccessToken);
}
public async Task SetRefreshToken(string newRefreshToken)
{
await _protectedLocalStorage.SetAsync("refreshToken", newRefreshToken);
}
}
I am trying to implement a custom authentication filter in Guice. I receive the token, get the username and realm from the token and then create a Principal. Now I am stuck and I don't know how to set the Principal. It would be nice if I could just set it like this request.setUserPrincipal(principal);, but obviously I can't.
How can I do this?
My doFilter method looks like this:
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
if (authorizationHeader != null && authorizationHeader.length() > 0) {
String token = authorizationHeader.substring("Bearer".length()).trim();
if (token.length() > 0) {
try {
Credentials credentials = securityService.getCredentials(token);
String username = credentials.getUsername();
String realm = credentials.getRealm();
Principal principal = new HttpPrincipal(username, realm);
// request.setUserPrincipal(principal);
LOGGER.info(credentials);
} catch (Exception e) {
LOGGER.error(e);
}
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
The servlet spec section 13.10 says:
The container establishes the caller identity of a request prior to
dispatching the request to the servlet engine. The caller identity
remains unchanged throughout the processing of the request or until
the application sucessfully calls authenticate, login or logout on the
request.
That is the reason why there is no setUserPrincipal.
But there are good news. You can provide your own getUserPrincipal because you can provide your own HttpServletRequest object. Any servlet filter can do it. Look at your code, you are calling the chain method with two parameters: the request and the response. There is no need to pass the same objects that you receive.
The spec even provides you with a helper class: HttpServletRequestWrapper. You just create your own request class as a subclass of the wrapper and override any method that you want, like getUserPrincipal.
I have created a ServiceStack service on top of Asp.Net that implements Basic authentication. Everything is working fine on the service routes. I am able to login and I get the session cookies which are validated on subsequent calls. I'm using an HttpClient for those requests.
I also have a SignalR Hub that runs on the same Asp.Net service, but the Principal is not authenticated on my Hub methods.
Basically what I need is for ServiceStack to intercept calls into my Hub and validate the session cookie and populate the Context.User.Identity and mark it as authenticated. If I can get that set up, a simple [Authorize] attribute on my hub will do the rest.
Here is a sample of my code:
// set up a HttpClient with a cookie container to hold the session cookie
var cookieJar = new CookieContainer();
var handler = new HttpClientHandler { CookieContainer = cookieJar, UseCookies = true, UseDefaultCredentials = false };
var client = new HttpClient(handler) { BaseAddress = _baseUri };
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, password))));
// do client login and get response with session cookie...
var response = client.PostAsync(...);
// add the cookies to the SignalR hub connection
var responseCookies = cookieJar.GetCookies(_baseUri);
var cookieContainer = new CookieContainer();
foreach (Cookie cookie in responseCookies)
{
cookieContainer.Add(cookie);
}
_hubConnection = new HubConnection(_baseUri.ToString()) { CookieContainer = cookieContainer };
After this setup, my session cookies are sent to the Hub on each invocation. Somehow I need for ServiceStack to intercept those requests and set the authenticated user.
Let ServiceStack do the authenication and persisting the user session. Then in the SignalR hub endpoints that need authentication put this code:
var cache = AppHostBase.Resolve<ICacheClient>();
var sess = cache.SessionAs<AuthUserSession>();
if (!sess.IsAuthenticated)
throw new AuthenticationException();
Johan's answer works but it is not very convenient to put this code to every method and if you put it in the hub constructor, it will fail in the web on page refresh with "Only ASP.NET Requests accessible via Singletons are supported" exception.
Instead, I have chosen to create a custom attribute that gives you more control on the hub and method call authorization.
Simplest attribute would look like this:
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class AuthorizeServiceStack : AuthorizeAttribute
{
public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
{
return CheckAuthorization();
}
public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
return CheckAuthorization();
}
private static bool CheckAuthorization()
{
var cache = AppHostBase.Resolve<ICacheClient>();
var sess = cache.SessionAs<AuthUserSession>();
return sess.IsAuthenticated;
}
}
As you can see, the code is the same as in Johan's answer but it will work in the web as well, ensuring that HttpContext.Current is not null when you are calling cache.SessionAs
I am using WCF to develop a RESTful Web Service that acts as a proxy to a set of stored procedures that, for security reasons, cannot be directly accessed by Internet-facing applications.
The class implementing the service's DataContract has a helper property that retrieves the name of the currently logged-in user, or generates an HTTP 401 Unauthorized response if no user is currently logged in.
[ServiceContract]
public class MyService
{
// The helper property
string UserName
{
get
{
WebOperationContext context = WebOperationContext.Current;
if (context != null)
{
string authHeader = context.IncomingRequest.Headers[HttpRequestHeader.Authorization];
if (authHeader != null && authHeader.StartsWith("Basic "))
{
string string64 = authHeader.Substring(6);
byte[] array64 = Convert.FromBase64String(string64);
string decoded = ASCIIEncoding.ASCII.GetString(array64);
string[] authParts = decoded.Split(':');
if (ValidateLogin(authParts[0] /*userName*/,
authParts[1] /*password*/))
return authParts[0];
}
}
OutgoingWebResponseContext outgoing = context.OutgoingResponse;
outgoing.StatusCode = HttpStatusCode.Unauthorized;
outgoing.Headers[HttpResponseHeader.WwwAuthenticate] = "Basic";
return null;
}
}
[OperationContract]
public int LengthOfUserName()
{
return UserName.Length;
}
}
However, when I attempt to log in with a valid user name and password, I still get an Unauthorized error. What's wrong with my code?
According to this documentation, process of receiving OAuth access token is straightforward. I would like to see a list of all available API endpoints that is ready to accept OAuth 2.0 access token. But for my current needs i would like to somehow receive username and email of a user using OAuth 2.0 access token.
I successfully can receive, for example, data from this endpoint:
https://www.google.com/m8/feeds/contacts/default/full
But unable to receive data from this endpoint:
https://www.googleapis.com/userinfo/email
I tried both header-base and querystring-base approaches of passing single access token. Here is a header i tried:
Authorization: OAuth My_ACCESS_TOKEN
And I even tried OAuth 1.0 version of Authorization header, but... in OAuth 2.0 we do not have secret access token, for instance. Google use bearer tokens in his implementation of OAuth 2.0, so no additional credentials are required.
Anyone successfully received username and email using Google OAuth 2.0?
I found the answer I was looking for. I had to convert PHP to MVC, but pretty easy:
http://codecri.me/case/430/get-a-users-google-email-address-via-oauth2-in-php/
My MVC Login sandbox code looks like the following.
(using JSON.Net http://json.codeplex.com/)
public ActionResult Login()
{
string url = "https://accounts.google.com/o/oauth2/auth?";
url += "client_id=<google-clientid>";
url += "&redirect_uri=" +
// Development Server :P
HttpUtility.UrlEncode("http://localhost:61857/Account/OAuthVerify");
url += "&scope=";
url += HttpUtility.UrlEncode("http://www.google.com/calendar/feeds/ ");
url += HttpUtility.UrlEncode("http://www.google.com/m8/feeds/ ");
url += HttpUtility.UrlEncode("http://docs.google.com/feeds/ ");
url += HttpUtility.UrlEncode("https://mail.google.com/mail/feed/atom ");
url += HttpUtility.UrlEncode("https://www.googleapis.com/auth/userinfo.email ");
url += HttpUtility.UrlEncode("https://www.googleapis.com/auth/userinfo.profile ");
url += "&response_type=code";
return new RedirectResult(url);
}
The code returned is proof of Authorization token from the user, which then needs to be turn into a Authentication (accessToken) to access resources.
My MVC OAuthVerify then looks like:
public ActionResult AgentVerify(string code)
{
JObject json;
if (!string.IsNullOrWhiteSpace(code))
{
NameValueCollection postData = new NameValueCollection();
postData.Add("code", code);
postData.Add("client_id", "<google-clientid>");
postData.Add("client_secret", "<google-client-secret>");
postData.Add("redirect_uri", "http://localhost:61857/Account/OAuthVerify");
postData.Add("grant_type", "authorization_code");
try
{
json = JObject.Parse(
HttpClient.PostUrl(
new Uri("https://accounts.google.com/o/oauth2/token"), postData));
string accessToken = json["access_token"].ToString();
string refreshToken = json["refresh_token"].ToString();
bool isBearer =
string.Compare(json["token_type"].ToString(),
"Bearer",
true,
CultureInfo.CurrentCulture) == 0;
if (isBearer)
{
json = JObject.Parse(
HttpClient.GetUrl(
new Uri("https://www.googleapis.com/oauth2/v1/userinfo?alt=json"),
accessToken));
string userEmail = json["email"].ToString();
}
return View("LoginGood");
}
catch (Exception ex)
{
ErrorSignal.FromCurrentContext().Raise(ex); //ELMAH
}
}
return View("LoginBad");
}
To complete how everything works, I've included the HttpClient utility I created in case anyone needed it.
public class HttpClient
{
public static string GetUrl(Uri url, string OAuth)
{
string result = string.Empty;
using (WebClient httpClient = new WebClient())
{
httpClient.Headers.Add("Authorization","OAuth " + OAuth);
result = httpClient.DownloadString(url.AbsoluteUri);
}
return result;
}
public static string PostUrl(Uri url, NameValueCollection formData)
{
string result = string.Empty;
using (WebClient httpClient = new WebClient())
{
byte[] bytes = httpClient.UploadValues(url.AbsoluteUri, "POST", formData);
result = Encoding.UTF8.GetString(bytes);
}
return result;
}
}
Again, this is test code just to get it to function, I do not recommend using this as-is in a production environment.
try this:
curl -k https://www.googleapis.com/userinfo/email -H "Authorization: OAuth 1/g5_039aCIAfEBuL7OCyB31n1URYU5tUIDudiWKuxN1o"
output: email=name#gmail.com&isVerified=tru