IdentityServer4 - End Session in POST - identityserver4

IdentityServer4 documentation explains how to use the end session endpoint using the HTTP method GET (https://identityserver4.readthedocs.io/en/latest/endpoints/endsession.html).
Is it possible to configure it in order to accept POST requests as well?

If you look at the source code here, it seems that the endpoint accepts both GET and POST requests.
if (HttpMethods.IsGet(context.Request.Method))
{
parameters = context.Request.Query.AsNameValueCollection();
}
else if (HttpMethods.IsPost(context.Request.Method))
{
parameters = (await context.Request.ReadFormAsync()).AsNameValueCollection();
}
else
{
_logger.LogWarning("Invalid HTTP method for end session endpoint.");
return new StatusCodeResult(HttpStatusCode.MethodNotAllowed);
}

Related

How to tell if a user is logged in with http only cookies and JWT in react (client-side)

So I'm trying to follow the security best practices and I'm sending my JWT token over my React app in a only-secure http-only cookie.
This works fine for requests but the major issue I find with this approach is, how can I tell if the user is logged-in on client-side if I can't check if the token exists? The only way I can think of is to make a simple http to a protected endpoint that just returns 200.
Any ideas? (not looking for code implementations)
The approach I would follow is to just assume the user is logged in, and make the desired request, which will send the httpOnly token automatically in the request headers.
The server side should then respond with 401 if the token is not present in the request, and you can then react in the client side accordingly.
Using an endpoint like /api/users/me
Server-side
Probably you don't only need to know if a user is already logged in but also who that user is. Therefore many APIs implement an endpoint like /api/users/me which authenticates the request via the sent cookie or authorization header (or however you've implemented your server to authenticate requests).
Then, if the request is successfully authenticated, it returns the current user. If the authentication fails, return a 401 Not Authorized (see Wikipedia for status codes).
The implementation could look like this:
// UsersController.ts
// [...]
initializeRoutes() {
this.router.get('users/me', verifyAuthorization(UserRole.User), this.getMe);
}
async getMe(req: Request, res: Response) {
// an AuthorizedRequest has the already verified JWT token added to it
const { id } = (req as AuthorizedRequest).token;
const user = await UserService.getUserById(id);
if (!user) {
throw new HttpError(404, 'user not found');
}
logger.info(`found user <${user.email}>`);
res.json(user);
}
// [...]
// AuthorizationMiddleware.ts
export function verifyAuthorization(expectedRole: UserRole) {
// the authorization middleware throws a 401 in case the JWT is invalid
return async function (req: Request, res: Response, next: NextFunction) {
const authorization = req.headers.authorization;
if (!authorization?.startsWith('Bearer ')) {
logger.error(`no authorization header found`);
throw new HttpError(401, 'unauthorized');
}
const token = authorization.split(' ')[1];
const decoded = AuthenticationService.verifyLoginToken(token);
if (!decoded) {
logger.warn(`token not verified`);
throw new HttpError(401, 'unauthorized');
}
(req as AuthorizedRequest).token = decoded;
const currentRole = UserRole[decoded.role] ?? 0;
if (currentRole < expectedRole) {
logger.warn(`user not authorized: ${UserRole[currentRole]} < ${UserRole[expectedRole]}`);
throw new HttpError(403, 'unauthorized');
}
logger.debug(`user authorized: ${UserRole[currentRole]} >= ${UserRole[expectedRole]}`);
next();
};
}
Client-side
If the response code is 200 OK and contains the user data, store this data in-memory (or as alternative in the local storage, if it doesn't include sensitive information).
If the request fails, redirect to the login page (or however you want your application to behave in that case).

Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter..?

I am trying to send a PUT request to an amazonS3 presigned URL. My request seems to be called twice even if I only have one PUT request. The first request returns 200 OK, the second one returns 400 Bad Request.
Here is my code:
var req = {
method: 'PUT',
url: presignedUrl,
headers: {
'Content-Type': 'text/csv'
},
data: <some file in base64 format>
};
$http(req).success(function(result) {
console.log('SUCCESS!');
}).error(function(error) {
console.log('FAILED!', error);
});
The 400 Bad Request error in more detail:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>InvalidArgument</Code>
<Message>Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified</Message>
<ArgumentName>Authorization</ArgumentName>
<ArgumentValue>Bearer someToken</ArgumentValue>
<RequestId>someRequestId</RequestId>
<HostId>someHostId</HostId>
</Error>
What I don't understand is, why is it returning 400? and What's the workaround?
Your client is probably sending an initial request that uses an Authorization header, which is being responded with a 302. The response includes a Location header which has a Signature parameter. The problem is that the headers from the initial request are being copied into the subsequent redirect request, such that it contains both Authorization and Signature. If you remove the Authorization from the subsequent request you should be good.
This happened to me, but in a Java / HttpClient environment. I can provide details of the solution in Java, but unfortunately not for AngularJS.
For the Googlers, if you're sending a signed (signature v4) S3 request via Cloudfront and "Restrict Bucket Access" is set to "Yes" in your Cloudfront Origin settings, Cloudfront will add the Authorization header to your request and you'll get this error. Since you've already signed your request, though, you should be able to turn this setting off and not sacrifice any security.
I know this may be too late to answer, but like #mlohbihler said, the cause of this error for me was the Authorization header being sent by the http interceptor I had setup in Angular.
Essentially, I had not properly filtered out the AWS S3 domain so as to avoid it automatically getting the JWT authorization header.
Also, the 400 "invalid argument" may surface as a result of wrong config/credentials for your S3::Presigner that is presigning the url to begin with. Once you get past the 400, you may encounter a 501 "not implemented" response like I did. Was able to solve it by specifying a Content-Length header (specified here as a required header). Hopefully that helps #arjuncc, it solved my postman issue when testing s3 image uploads with a presigned url.
The message says that ONLY ONE authentication allowed. It could be that You are sending one in URL as auth parameters, another - in headers as Authorization header.
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:scavenger_inc_flutter/utils/AuthUtils.dart';
import 'package:scavenger_inc_flutter/utils/URLS.dart';
class ApiClient {
static Dio dio;
static Dio getClient() {
if (dio == null) {
dio = new Dio();
dio.httpClientAdapter = new CustomHttpAdapter();
}
return dio;
}
}
class CustomHttpAdapter extends HttpClientAdapter {
DefaultHttpClientAdapter _adapter = DefaultHttpClientAdapter();
#override
void close({bool force = false}) {
_adapter.close(force: force);
}
#override
Future<ResponseBody> fetch(RequestOptions options,
Stream<List<int>> requestStream, Future<dynamic> cancelFuture) async {
String url = options.uri.toString();
if (url.contains(URLS.IP_ADDRESS) && await AuthUtils.isLoggedIn()) {
options.followRedirects = false;
options.headers.addAll({"Authorization": await AuthUtils.getJwtToken()});
}
final response = await _adapter.fetch(options, requestStream, cancelFuture);
if (response.statusCode == 302 || response.statusCode == 307) {
String redirect = (response.headers["location"][0]);
if(!redirect.contains(URLS.IP_ADDRESS)) {
options.path = redirect;
options.headers.clear();
}
return await fetch(options, requestStream, cancelFuture);
}
return response;
}
}
I disallowed following redirects.
Used the response object to check if it was redirected.
If it was 302, or 307, (HTTP Redirect Codes), I resent the request after clearing the Auth Headers.
I used an additioal check to send the headers only if the path contained my specific domain URL (or IP Address in this example).
All of the above, using a CustomHttpAdapter in Dio. Can also be used for images, by changing the ResponseType to bytes.
Let me know if this helps you!
I was using django restframework. I applied Token authentication in REST API. I use to pass token in request header (used ModHeader extension of Browser which automatically put Token in Authorization of request header) of django API till here every thing was fine.
But while making a click on Images/Files (which now shows the s3 URL). The Authorization automatically get passed. Thus the issue.
Link look similar to this.
https://.s3.amazonaws.com/media//small_image.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXXXXXXXXXXXXXXXXXXXX%2F20210317%2Fap-south-XXXXXXXXFaws4_request&X-Amz-Date=XXXXXXXXXXXXXXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
I lock the ModHeader extension to pass Authorization Token only while making rest to REST API and not while making resquest to S3 resources. i.e. do not pass any other Authorization while making request to S3 resource.
It's a silly mistake. But in case it helps.
Flutter: if you experience this with the http dart package, then upgrade to Flutter v2.10!
Related bugs in dart issue tracker:
https://github.com/dart-lang/sdk/issues/47246
https://github.com/dart-lang/sdk/issues/45410
--> these has been fixed in dart 2.16, which has been shipped with Flutter v2.10!
https://medium.com/dartlang/dart-2-16-improved-tooling-and-platform-handling-dd87abd6bad1

Building realtime app using Laravel and Latchet websocket

I'm building a closed app (users need to authenticate in order to use it). I'm having trouble in identifying the currently authenticated user from my Latchet session. Since apache does not support long-lived connections, I host Latchet on a separate server instance. This means that my users receive two session_id's. One for each connection. I want to be able to identify the current user for both connections.
My client code is a SPA based on AngularJS. For client WS, I'm using the Autobahn.ws WAMP v1 implementation. The ab framework specifies methods for authentication: http://autobahn.ws/js/reference_wampv1.html#session-authentication, but how exactly do I go about doing this?
Do I save the username and password on the client and retransmit these once login is performed (which by the way is separate from the rest of my SPA)? If so, won't this be a security concearn?
And what will receive the auth request server side? I cannot find any examples of this...
Please help?
P.S. I do not have reputation enough to create the tag "Latchet", so I'm using Ratchet (which Latchet is built on) instead.
Create an angularjs service called AuthenticationService, inject where needed and call it with:
AuthenticationService.check('login_name', 'password');
This code exists in a file called authentication.js. It assumes that autobahn is already included. I did have to edit this code heavily removing all the extra crap I had in it,it may have a syntax error or two, but the idea is there.
angular.module(
'top.authentication',
['top']
)
.factory('AuthenticationService', [ '$rootScope', function($rootScope) {
return {
check: function(aname, apwd) {
console.log("here in the check function");
$rootScope.loginInfo = { channel: aname, secret: apwd };
var wsuri = 'wss://' + '192.168.1.11' + ':9000/';
$rootScope.loginInfo.wsuri = wsuri;
ab.connect(wsuri,
function(session) {
$rootScope.loginInfo.session = session;
console.log("connected to " + wsuri);
onConnect(session);
},
function(code,reason) {
$rootScope.loginInfo.session = null;
if ( code == ab.CONNECTION_UNSUPPORTED) {
console.log(reason);
} else {
console.log('failed');
$rootScope.isLoggedIn = 'false';
}
}
);
function onConnect(sess) {
console.log('onConnect');
var wi = $rootScope.loginInfo;
sess.authreq(wi.channel).then(
function(challenge) {
console.log("onConnect().then()");
var secret = ab.deriveKey(wi.secret,JSON.parse(challenge).authextra);
var signature = sess.authsign(challenge, secret);
sess.auth(signature).then(onAuth, ab.log);
},ab.log
);
}
function onAuth(permission) {
$rootScope.isLoggedIn = 'true';
console.log("authentication complete");
// do whatever you need when you are logged in..
}
}
};
}])
then you need code (as you point out) on the server side. I assume your server side web socket is php coding. I can't help with that, haven't coded in php for over a year. In my case, I use python, I include the autobahn gear, then subclass WampCraServerProtocol, and replace a few of the methods (onSessionOpen, getAuthPermissions, getAuthSecret, onAuthenticated and onClose) As you can envision, these are the 'other side' of the angular code knocking at the door. I don't think autobahn supports php, so, you will have to program the server side of the authentication yourself.
Anyway, my backend works much more like what #oberstat describes. I establish authentication via old school https, create a session cookie, then do an ajax requesting a 'ticket' (which is a temporary name/password which i associate with the web authenticated session). It is a one use name/password and must be used in a few seconds or it disappears. The point being I don't have to keep the user's credentials around, i already have the cookie/session which i can create tickets that can be used. this has a neat side affect as well, my ajax session becomes related to my web socket session, a query on either is attributed to the same session in the backend.
-g
I can give you a couple of hints regarding WAMP-CRA, which is the authentication mechnism this is referring:
WAMP-CRA does not send passwords over the wire. It works by a challenge-response scheme. The client and server have a shared secret. To authenticate a client, the server will send a challenge (something random) that the client needs to sign - using the secret. And only the signature is sent back. The client might store the secret in browser local storage. It's never sent.
In a variant of above, the signing of the challenge the server sends is not directly signed within the client, but the client might let the signature be created from an Ajax request. This is useful when the client was authenticated using other means already (e.g. classical cookie based), and the signing can then be done in the classical web app that was authenticating.
Ok, Greg was kind enough to provide a full example of the client implementation on this, so I wont do anything more on that. It works with just a few tweaks and modifications to almost any use-case I can think of. I will mark his answer as the correct one. But his input only covered the theory of the backend implementation, so I will try to fill in the blanks here for postparity.
I have to point out though, that the solution here is not complete as it does not give me a shared session between my SPA/REST connection and my WS connection.
I discovered that the authentication request transmitted by autobahn is in fact a variant of RPC and for some reason has hardcoded topic names curiously resembling regular url's:
- 'http://api.wamp.ws/procedure#authreq' - for auth requests
- 'http://api.wamp.ws/procedure#auth' - for signed auth client responses
I needed to create two more routes in my Laravel routes.php
// WS CRA routes
Latchet::topic('http://api.wamp.ws/procedure#authreq', 'app\\socket\\AuthReqController');
Latchet::topic('http://api.wamp.ws/procedure#auth', 'app\\socket\\AuthReqController');
Now a Latchet controller has 4 methods: subscribe, publish, call and unsubscribe. Since both the authreq and the auth calls made by autobahn are RPC calls, they are handled by the call method on the controller.
The solution first proposed by oberstet and then backed up by Greg, describes a temporary auth key and secret being generated upon request and held temporarily just long enough to be validated by the WS CRA procedure. I've therefore created a REST endpoint which generates a persisted key value pair. The endpoint is not included here, as I am sure that this is trivial.
class AuthReqController extends BaseTopic {
public function subscribe ($connection, $topic) { }
public function publish ($connection, $topic, $message, array $exclude, array $eligible) { }
public function unsubscribe ($connection, $topic) { }
public function call ($connection, $id, $topic, array $params) {
switch ($topic) {
case 'http://api.wamp.ws/procedure#authreq':
return $this->getAuthenticationRequest($connection, $id, $topic, $params);
case 'http://api.wamp.ws/procedure#auth':
return $this->processAuthSignature($connection, $id, $topic, $params);
}
}
/**
* Process the authentication request
*/
private function getAuthenticationRequest ($connection, $id, $topic, $params) {
$auth_key = $params[0]; // A generated temporary auth key
$tmpUser = $this->getTempUser($auth_key); // Get the key value pair as persisted from the temporary store.
if ($tmpUser) {
$info = [
'authkey' => $tmpUser->username,
'secret' => $tmpUser->secret,
'timestamp' => time()
];
$connection->callResult($id, $info);
} else {
$connection->callError($id, $topic, array('User not found'));
}
return true;
}
/**
* Process the final step in the authentication
*/
private function processAuthSignature ($connection, $id, $topic, $params) {
// This should do something smart to validate this response.
// The session should be ours right now. So store the Auth::user()
$connection->user = Auth::user(); // A null object is stored.
$connection->callResult($id, array('msg' => 'connected'));
}
private function getTempUser($auth_key) {
return TempAuth::findOrFail($auth_key);
}
}
Now somewhere in here I've gone wrong. Cause if I were supposed to inherit the ajax session my app holds, I would be able to call Auth::user() from any of my other WS Latchet based controllers and automatically be presented with the currently logged in user. But this is not the case. So if somebody see what I'm doing wrong, give me a shout. Please!
Since I'm unable to get the shared session, I'm currently cheating by transmitting the real username as a RPC call instead of performing a full CRA.

AngularJS and OWIN Authentication on WebApi

I have implemented OWIN token based authentication on my WebApi, I have also enabled CORS by calling app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll)
I can access various unsecured portions of my app from an angularjs web client. I have used this http-interceptor , when I try to access a protected resource, I get my login pop.
Now in order to login I have to call http://mywebapi/token with form encoded UserName Password and grant_type, see my header signature below (from chrome)
Request URL:http://mywebapi/token
Request Headers CAUTION: Provisional headers are shown.
Accept:application/json, text/plain, */*
cache:false
Content-Type:application/x-www-form-urlencoded
Origin:http://127.0.0.1:49408
Referer:http://127.0.0.1:49408/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36
Form Dataview sourceview URL encoded
grant_type:password
UserName:correctuser
Password:Password
When I use postman to send this request, it comes back fine with the expected accesstoken, however when I try to use angular's $http service, it makes the OPTIONS request (I can see this in Dev tools console) but for some reason I get this error message
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:49408' is therefore not allowed access.
NOTE: This only happens for the /token request which is form-url-encoded, for all other json requests the header is added as expected. Can someone please help, I am running out of ideas.
Thanks
I went through the same process and spend (wasted?) the same amount of time as most people, dealing with owin + web api.
A solution which worked for me was to move
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
before everything else in the pipe.
Here is some code:
OwinStartup
[assembly: OwinStartup(typeof(MyApp.Web.Startup))]
namespace MyApp.Web
{
using Owin;
using Microsoft.Owin;
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new System.Web.Http.HttpConfiguration();
ConfigureAuth(app, config);
}
}
}
Startup for OAuth
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app, System.Web.Http.HttpConfiguration config)
{
// app.UseWelcomePage("/");
// app.UseErrorPage();
// Must be the first to be set otherwise it won't work.
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.CreatePerOwinContext<ApplicationDatabaseContext>(ApplicationDatabaseContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
var OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new DaufAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleAuthorizationServerProvider(),
};
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseWebApi(WebApiConfig.Register(config, logger));
}
}
Web Api
public static class WebApiConfig
{
public static HttpConfiguration Register(System.Web.Http.HttpConfiguration config, ILogger logger)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
// This will used the HTTP header: "Authorization" Value: "Bearer 1234123412341234asdfasdfasdfasdf"
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
return (config);
}
}
So I found the answer but brace yourself 'coz this one's weird!! I read this article on code project which led me to my Owin Authorisation server's GrantResourceOwnerCredentials method to check for this
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
(Mine is a custom Authoris(z)ation server, one I nicked off here)
The shocking thing I found was that it was already there!
So I decided to set a break point on that line and what do you know, that line was failing because (...even more shocking) "Access-Control-Allow-Origin" was already in the headers!!
So I commented that line out and it all worked! But then the caveat, I have no idea how the header got in, so I have no idea if it will be there or not in production so I swapped that line of code with this to check and then add it if it was not already there
var header = context.OwinContext.Response.Headers.SingleOrDefault(h => h.Key == "Access-Control-Allow-Origin");
if (header.Equals(default(KeyValuePair<string, string[]>)))
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
}
I hope my labour of love will save a few souls from the excruciating damnation of countless hours of tinkering with nothing to solve this problem. Cheers!
For those curious about the answer and the previous answer, it is indeed strongly related the ordering. Whenever you are adding Owin middleware it is important to note: The order of registration is imperative.
app.useCors(Microsoft.Owin.Cors.CorsOptions.AllowAll)
Having this as the first thing in your auth file, basically registers the Cors handler to occur prior to reaching your OAuthServer and Web Api.
Moving it after the OAuth does the opposite, hence the need to add the Access-Control-Allow-Origin header in the GrantResourceOwnerCredentials.
To answer the other question, the reason the header is already there is if you send a CORS request from the browser and the CorsOptions.AllowAll is specified, it adds one for you so by the time it reaches the /token endpoint on the OAuth server it has already added one. (just means that one was found in the http request and you are allowing all origins).
You can verify the behaviours accordingly,
In Fiddler, send a new request to your Token endpoint with an Origin header included with an arbitrary value. Put a breakpoint on your OAuth server in the GrantResourceOwnerCredentials and then examine context.Response.Headers, it will now contain the origin you passed in. (Remember, the browser must examine it, fiddler will be happy all day long)
If you then tell CORS not to use CorsOptions.AllowAll and set AllowAnyOrigin to false you will notice that the Origin sent from Fiddler is no longer added to the response headers.
The browser in turn will deny the CORS request because the origin was not returned - Origin "" not found in Access-Control-Allow-Origin header.
NOW FOR THE IMPORTANT BIT:
If you set CorsOptions.AllowAll it does exactly what it says it does, allows CORS requests to any method on any middleware that occurs after the CORS registration in the Owin pipeline so make sure that is what you intend to do. IE: If you register CORS first then OAuth and Web API then all your Web API methods will be accessible via CORS if you do not explicitly add code\attributes to prevent it.
If you want to restrict the methods then implement an ICorsPolicyProvider, some portions from http://katanaproject.codeplex.com/(Microsoft.Owin.Cors)
public class MyCorsPolicyProvider : ICorsPolicyProvider
{
public Task<CorsPolicy> GetCorsPolicyAsync(IOwinRequest request)
{
// Grant Nothing.
var policy = new CorsPolicy
{
AllowAnyHeader = false,
AllowAnyMethod = false,
AllowAnyOrigin = false,
SupportsCredentials = false
};
// Now we can get a bit clever: (Awesome, they requested the token endpoint. Setup OAuth options for that.
if (OAuthOptions.TokenEndpointPath.HasValue && OAuthOptions.TokenEndpointPath == request.Path)
{
// Hypothetical scenario, tokens can only be obtained using CORS when the Origin is http://localhost
policy.AllowAnyHeader = true;
policy.AllowAnyMethod = true;
policy.AllowAnyOrigin = false;
policy.SupportsCredentials = true;
policy.Origins.Add("http://localhost");
return Task.FromResult(policy);
}
// No token?, must already have one.... so this must be a WebApi request then.
// From here we could check where the request is going, do some other fun stuff etc... etc...
// Alternatively, do nothing, set config.EnableCors() in WebApi, then apply the EnableCors() attribute on your methods to allow it through.
return null; }
}
The return null; tells Owin to continue to the next middleware and to allow the request through but with no policy thus NO CORS!, allowing you to set appropriate CORS attributes in WebAPI
Now the really important bit, DO NOT add the Access-Control-Allow-Origins header to your response if it is not there unless that is really what you intend as depending on your middleware registration order it will open all the doors for CORS requests unless you explicitly block them elsewhere or remove the header and basically will cause you lots of issues when you try and use CORS with WebApi and want to restrict it.
To block them elsewhere you could add a CorsPolicyProvider (System.Web.Http) for WebApi then set a Context variable in Owin which you can read once the request hits WebApi.
public class WebApiCorsPolicyProvider : System.Web.Http.Cors.ICorsPolicyProvider
{
public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var policy = new CorsPolicy
{
AllowAnyHeader = false,
AllowAnyMethod = false,
AllowAnyOrigin = false,
SupportsCredentials = false
};
// The benefit of being at this point in the pipeline is we have been authenticated\authorized so can check all our claims for CORS purposes too if needed and set errors etc...
// In an Owin pipeline?
var owinContext = request.GetOwinContext();
if (owinContext != null)
{
// We have an owin pipeline, we can get owin parameters and other things here.
}
else
{
// Write your code here to determine the right CORS options. Non Owin pipeline variant.
}
return Task.FromResult(policy);
}
}
And finally, one other benefit of propagating downwards to a WebApi CORS policy provider is that at that point Authorization will have taken place so you can then apply additional Origin filters at that stage in the CORS policy provider.
In my opinion it is related to ordering of your statements though I did not investigated further. I faced the same issue and tried all combinations and eventually following worked for me.
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
I was following Token Based Authentication using ASP.NET Web API 2, Owin, and Identity
This is another version of the code for the Obi Onuorah's response
string corsHeader = "Access-Control-Allow-Origin";
if (!context.Response.Headers.ContainsKey(corsHeader))
{
context.Response.Headers.Add(corsHeader, new[] { "*" });
}

Unauthenticated call to Endpoint working for an API method with authentication

I am facing issue with Endpoints authentication.
This is the api code I am using
#ApiMethod(name = "myapiname.myapimethod", scopes = { Constants.EMAIL_SCOPE }, clientIds = {
Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID,
Constants.API_EXPLORER_CLIENT_ID }, audiences = { Constants.ANDROID_AUDIENCE })
public Result myapimethod(User user) throws OAuthRequestException, IOException {
// some work
return new Result();
}
In API explorer, it shows that the method requires Authorization, but it is getting successfully executed even without authorizing the request in API explorer.
Any help will be highly appreciated.
Thanks in advance.
Adding an User parameter alone won't check if the endpoint request is authenticated, we need to check that ourselves refer the documentation https://developers.google.com/appengine/docs/java/endpoints/auth#Java_Adding_a_user_parameter_to_methods_for_auth
`If an incoming client request has no authorization token or an invalid one, user is null. In your code, you need to check whether user is null and do ONE of the following, depending on the condition:
If the user is present, perform the authorized action.
If the user is null, throw an OAuthRequestException.
Alternatively, if the user is null, perform some action for an unauthorized client access if some sort of unauthorized access is desired.`

Resources