I need to add an aspx page into my mvc project, but I always get 404. I founded that in web.config there is owin http handler registration
<add name="Owin" verb="" path="*" type="Microsoft.Owin.Host.SystemWeb.OwinHttpHandler, Microsoft.Owin.Host.SystemWeb" />
If I commented this my aspx page works fine.
So where can be problem?
Here is my Startup's code:
public void Configuration(IAppBuilder app)
{
app.UseStaticFiles();
Dictionary<string, string> mapping = new Dictionary<string, string>
{
{".apk", "application/vnd.android.package-archive"}
};
StaticFileOptions options = new StaticFileOptions
{
ContentTypeProvider = new FileExtensionContentTypeProvider(mapping)
};
app.UseStaticFiles(options);
LogProvider.SetCurrentLogProvider(new Log4NetLogProvider());
HttpConfiguration config = new HttpConfiguration();
ODataConventionModelBuilder builder = new ODataConventionModelBuilder
{
Namespace = "blabla",
ContainerName = "blabla"
};
builder.EntitySet<Common.DataAccess.Sharepoint.Users>("Users");
var departmentSet = builder.EntitySet<Common.DataAccess.Sharepoint.Departments>("Department");
builder.EntitySet<Common.DataAccess.Sharepoint.Roles>("Roles");
var dependentFunction = departmentSet.EntityType.Collection.Function("GetChilds");
dependentFunction.Parameter<int>("parent");
dependentFunction.ReturnsCollectionFromEntitySet<Common.DataAccess.Sharepoint.Departments>("Department");
var allUsers = departmentSet.EntityType.Collection.Function("GetAllDependenUsers");
allUsers.Parameter<int>("department");
allUsers.Parameter<string>("query");
allUsers.ReturnsCollectionFromEntitySet<Common.DataAccess.Sharepoint.Users>("Users");
config.MapODataServiceRoute("orgstructure", "orgstructure", builder.GetEdmModel());
app.UseWebApi(config);
AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
SecureString password = new SecureString();
foreach (char c in "idsrv3test")
password.AppendChar(c);
IdentityServerOptions identityServerOptions = new IdentityServerOptions
{
SiteName = "123123",
Factory = new IdentityServerServiceFactory
{
UserService = new Registration<IUserService>(typeof (IdentityUserService)),
ScopeStore = new Registration<IScopeStore>(typeof (IdentityScopeStore)),
ClientStore = new Registration<IClientStore>(typeof (IdentityClientStore))
},
RequireSsl = false,
CspOptions = new CspOptions
{
Enabled = false
},
SigningCertificate = new X509Certificate2(Assets.idsrv3test, password),
CorsPolicy = CorsPolicy.AllowAll,
AuthenticationOptions = {EnablePostSignOutAutoRedirect = true}
};
identityServerOptions.Factory.Register(new Registration<IUserRoleService, UserRolesService>());
identityServerOptions.Factory.Register(new Registration<IUserDepartmentsService, UserDepartmentsService>());
app.Map("/open_id", idsrvApp =>
{
identityServerOptions.Factory.ConfigureDefaultViewService(new DefaultViewServiceOptions
{
CacheViews = true,
ViewLoader = new Registration<IViewLoader, IdentityViewLoader>()
});
idsrvApp.UseIdentityServer(identityServerOptions);
});
app.UseCookieAuthentication(new CookieAuthenticationOptions {AuthenticationType = "Cookies"});
string baseUrl = ConfigurationManager.AppSettings["RedirectUrl"];
string clientName = ConfigurationManager.AppSettings["ClientName"];
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(Constants.StandardScopes.OpenId);
stringBuilder.Append(" ");
stringBuilder.Append(Common.Constants.Scopes.Portal);
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = baseUrl + "open_id",
ClientId = clientName,
Scope = stringBuilder.ToString(),
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies",
RedirectUri = baseUrl,
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
var id = n.AuthenticationTicket.Identity;
// create new identity and set name and role claim type
var nid = new ClaimsIdentity(id.AuthenticationType, ClaimTypes.GivenName, ClaimTypes.Role);
IEnumerable<Claim> claims = id.Claims
.Where(x => x.Type != Common.Constants.Claims.Role && x.Type != Common.Constants.Claims.PortalRole);
nid.AddClaims(claims);
nid.AddClaims(id.Claims.Where(x => x.Type == Common.Constants.Claims.Role)
.Select(x => new Claim(ClaimTypes.Role, x.Value, x.ValueType, x.Issuer, x.OriginalIssuer))
);
nid.AddClaims(id.Claims.Where(x => x.Type == Common.Constants.Claims.PortalRole)
.Select(x => new Claim(ClaimTypes.Role, x.Value, x.ValueType, x.Issuer, x.OriginalIssuer))
);
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
n.AuthenticationTicket = new AuthenticationTicket(nid, n.AuthenticationTicket.Properties);
return Task.FromResult(new object());
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.AuthenticationRequest)
{
var baseAddress = new Uri(n.ProtocolMessage.IssuerAddress);
if (baseAddress.Host != n.Request.Uri.Host)
{
var uriBuilder = new UriBuilder(baseAddress) {Host = n.Request.Uri.Host};
n.ProtocolMessage.IssuerAddress = uriBuilder.Uri.ToString();
}
var redirectBaseAddress = new Uri(n.ProtocolMessage.RedirectUri);
if (redirectBaseAddress.Host != n.Request.Uri.Host)
{
var uriBuilder = new UriBuilder(redirectBaseAddress) { Host = n.Request.Uri.Host };
n.ProtocolMessage.RedirectUri = uriBuilder.Uri.ToString();
}
}
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;
n.ProtocolMessage.IdTokenHint = idTokenHint;
}
return Task.FromResult(new object());
}
}
});
}
As long as you will use OwinHttpHandler you will not be able to serve aspx pages. This happens because instead of letting aspx request to reach webforms handler, you impose it to be processed by OwinHttpHandler that doesn't know how to handle aspx. So the solution, as you empirically saw, is to eliminate handler adding from web.config.
Related
Basically, I can log in just fine in my Client and access all pages. If I log out, I can't access anything on my client, it always redirects to my login page. Directly accessing the api endpoint is another matter. Say I call mylocalapp.com/api/users, directly from the browser url bar, I can still get all data. So I added [Authorize]. Unexpectedly, I get a 401 if I do that, DESPITE being logged in. So I checked HttpContext.User and simply User.Identity and they're empty, despite successful authentication. ClaimsIdentity is also empty. Can you identify anything that I might have done wrong? According to what I found on Google, this IS how it should be done. Thanks for your help.
Login method:
Takes user data, checks if ok, creates a JWT token and gets browser cookies (if null - session expired) then appends the token to the browser. And then logs the user in via Sign In Manager. If ok, returns ok. Context var is just for debugging purposes.
[AllowAnonymous]
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
async public Task<IActionResult> LoginPost(AuthUser authUser)
{
var user = _context.Users.FirstOrDefault(user => user.Email == authUser.Email);
if (authUser is null) return BadRequest(AppResources.NullUser);
if (user is null) return BadRequest(AppResources.UserBadCredentials);
else
{
var isPassword = _userManager.CheckPasswordAsync(user, authUser.Password);
if (!isPassword.Result) return BadRequest(AppResources.UserBadCredentials);
var token = _jwtHandlerAuth.Authentication(authUser);
if (token == null) return BadRequest(AppResources.UserAuthenticationImpossible);
string cookieValue = Request.Cookies["jwt"];
var returnUser = new ReturnUser
{
Email = user.Email,
Name = $"{user.FirstName} {user.LastName}",
UserName = user.UserName
};
if (cookieValue != token)
{
Response.Cookies.Append("jwt", token, new CookieOptions
{
HttpOnly = true,
IsEssential = true,
SameSite = SameSiteMode.None,
Secure = true
});
}
var signInResult = await _signInManager.PasswordSignInAsync(authUser.Email, authUser.Password, false, false);
if (!signInResult.Succeeded) return BadRequest(AppResources.UserAuthenticationImpossible);
var context = _signInManager.Context;
return Ok(returnUser);
}
}
My Authentication method (creates the token):
public string Authentication(AuthUser authUser)
{
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim("UserName", authUser.Email),
new Claim("Email", authUser.Email)
}),
Expires = DateTime.UtcNow.AddHours(2),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF32.GetBytes(_privateKey)),
SecurityAlgorithms.HmacSha256Signature)
};
var claimsPrincipal = new ClaimsPrincipal(tokenDescriptor.Subject);
var token = tokenHandler.CreateToken(tokenDescriptor);
tokenHandler.WriteToken(token);
return tokenHandler.WriteToken(token);
}
And my Startup class services and middleware:
var builder = services.AddIdentityCore<User>();
var identityBuilder = new IdentityBuilder(builder.UserType, builder.Services);
identityBuilder.AddRoles<IdentityRole>();
identityBuilder.AddEntityFrameworkStores<IdentityContext>();
identityBuilder.AddSignInManager<SignInManager<User>>();
identityBuilder.AddDefaultTokenProviders();
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
x.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddCookie("Identity.Application").AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF32.GetBytes(Configuration.GetSection("Jwt:PrivateKey").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddSingleton<IJwtHandlerAuth>(new JwtHandlerAuth(Configuration.GetSection("Jwt:PrivateKey").Value));
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
I know this issue has been reported alot and there are a lot of articles on how to fix this. I've gone through a lot of them and still can't solve this.
I am setting custom claims on the Principal.Identity from within the OnSecurityTokenValidated callback in Identity Server like so:
public async Task SecurityTokenValidated(SecurityTokenValidatedContext context) {
var identity = context.Principal.Identity as ClaimsIdentity;
foreach(var claim in context.Principal.Claims.Where(x = >x.Type == "adGroupClaimType").ToList()) {
var groupName = Configuration.GetSection("ClaimMappings").GetValue < string > ($ "Prefix_{claim.Value}");
if (!string.IsNullOrWhiteSpace(groupName)) {
identity.AddClaim(new Claim(ClaimTypes.Role, groupName));
}
identity.RemoveClaim(claim);
}
}
The following is the configuration I use in Identity Server:
public static IEnumerable < IdentityResource > IdentityResources = >new List < IdentityResource > {
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResource("roles", new[] {
ClaimTypes.Role
})
};
public static IEnumerable < ApiScope > ApiScopes = >new List < ApiScope > {
new ApiScope("api", "API")
};
public static IEnumerable < Client > Clients = >new List < Client > {
new Client {
ClientId = "mvc-openid",
ClientSecrets = {
new Secret("secret".Sha256())
},
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RedirectUris = {
"https://localhost:6001/signin-oidc"
},
AllowedScopes = new List < string > {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api",
"roles"
},
RequirePkce = false,
AllowOfflineAccess = true,
AllowAccessTokensViaBrowser = true
}
};
There's an MVC client which connects to the Identity Server using OpenIdConnect. The code is given below:
public void ConfigureServices(IServiceCollection services) {
services.AddMvc();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddAuthentication(options = >{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
}).AddCookie(options = >{
options.ExpireTimeSpan = 15;
options.SlidingExpiration = true;
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
}).AddOpenIdConnect("oidc", options = >{
options.Authority = "https://localhost:5001";
options.ClientId = "mvc-openid";
options.ClientSecret = "secret";
options.ResponseType = "code id_token token";
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.Scope.Add("api");
options.Scope.Add("roles");
options.Scope.Add("offline_access");
options.Events.OnTokenValidated = OnTokenValidated;
options.TokenValidationParameters = new TokenValidationParameters {
NameClaimType = ClaimTypes.Name,
RoleClaimType = ClaimTypes.Role
};
options.ClaimActions.MapUniqueJsonKey(ClaimTypes.Role, ClaimTypes.Role);
});
}
private Task OnTokenValidated(TokenValidatedContext context) {
var t = context.Principal.Claims;
return Task.CompletedTask;
}
When I inspect the claims inside OnTokenValidated, I could see that all the role claims that I set from Identity Server are missing. Can someone tell me where I am going wrong? I have tried almost everything found on the Stack Overflow threads about this issue. Clueless right now as to what to do next.
I have also only found this solution so far.
options.Events.OnUserInformationReceived = context =>
{
var roleElement = context.User.RootElement.GetProperty("role");
var claims = new List<Claim>();
if (roleElement.ValueKind == System.Text.Json.JsonValueKind.Array)
{
foreach (var r in roleElement.EnumerateArray())
claims.Add(new Claim(JwtClaimTypes.Role, r.GetString()));
}
else
{
claims.Add(new Claim(JwtClaimTypes.Role, roleElement.GetString()));
}
var id = context.Principal.Identity as ClaimsIdentity;
id.AddClaims(claims);
return Task.CompletedTask;
};
https://github.com/skoruba/IdentityServer4.Admin/issues/109
I am trying to implement Single Sign On on my old WebForm app. The authentication part succeeds and I receive the access token. In the moment that I try to contact IdentityServer once again with the access token and get further claims regarding the logged user, then I have a problem. My code at startup looks like this:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "http://localhost:50001",
RedirectUri = "http://localhost:65438/signin-oidc",
PostLogoutRedirectUri = "http://localhost:65438/signout-callback-oidc",
RequireHttpsMetadata = false,
ClientId = "webforms",
AuthenticationType = "oidc",
SignInAsAuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
ResponseType = "id_token token",
Scope = "openid profile email",
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async n =>
{
var claimsToExclude = new[]
{
"aud", "iss", "nbf", "exp", "nonce", "iat", "at_hash"
};
var claimsToKeep = n.AuthenticationTicket.Identity.Claims.Where(x => !claimsToExclude.Contains(x.Type)).ToList();
claimsToKeep.Add(new Claim("id_token", n.ProtocolMessage.IdToken));
if (n.ProtocolMessage.AccessToken != null)
{
claimsToKeep.Add(new Claim("access_token", n.ProtocolMessage.AccessToken));
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:50001");
var userInfoResponse = await client.GetUserInfoAsync(new UserInfoRequest
{
Address = disco.UserInfoEndpoint,
Token = n.ProtocolMessage.AccessToken
});
var userInfoClaims = userInfoResponse.Claims
.Where(x => x.Type != "sub"); // filter sub since we're already getting it from id_token
claimsToKeep.AddRange(userInfoClaims);
}
var ci = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType, "name", "role");
ci.AddClaims(claimsToKeep);
n.AuthenticationTicket = new Microsoft.Owin.Security.AuthenticationTicket(ci, n.AuthenticationTicket.Properties);
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
n.ProtocolMessage.IdTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token")?.Value;
}
return Task.FromResult(0);
}
}
});
The specific part where the problem happens is this:
if (n.ProtocolMessage.AccessToken != null)
{
claimsToKeep.Add(new Claim("access_token", n.ProtocolMessage.AccessToken));
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:50001");
var userInfoResponse = await client.GetUserInfoAsync(new UserInfoRequest
{
Address = disco.UserInfoEndpoint,
Token = n.ProtocolMessage.AccessToken
});
var userInfoClaims = userInfoResponse.Claims
.Where(x => x.Type != "sub"); // filter sub since we're already getting it from id_token
claimsToKeep.AddRange(userInfoClaims);
}
Since this is in a middleware it's not very easy to debug it but the exception happening is this one:
Method not found: 'System.Threading.Tasks.Task`1<IdentityModel.Client.UserInfoResponse> IdentityModel.Client.HttpClientUserInfoExtensions.GetUserInfoAsync(System.Net.Http.HttpMessageInvoker, IdentityModel.Client.UserInfoRequest, System.Threading.CancellationToken)'.
Any suggestions? Thanks in advance!
When I auth using oidc I get back a bunch of claims. If I do not add my custom IProfileService all of these claims are passed through in the id_token that identity server issues. If I provide my own ProfileService, the list of claims on the Subject is a subset of what comes back from the idp. Is there any way to get the full list in the profile service?
Here is the relevant info from Startup.cs:
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
}).AddProfileService<ProfileService>();
services.AddAuthentication()
.AddOpenIdConnect("Name", "Name", o =>
{
o.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
o.SignOutScheme = IdentityServerConstants.SignoutScheme;
o.Authority = "https://sub.domain.com/adfs/";
o.ClientId = "00000000-0000-0000-0000-000000000000";
o.ClientSecret = "secret";
o.ResponseType = "id_token";
o.SaveTokens = true;
o.CallbackPath = "/signin-adfs";
o.SignedOutCallbackPath = "/signout-callback-adfs";
o.RemoteSignOutPath = "/signout-adfs";
o.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
and my ProfileService:
public class ProfileService : IProfileService
{
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var objectGuidClaim = context.Subject.Claims.FirstOrDefault(x => x.Type == "ObjectGUID");
if (objectGuidClaim != null)
{
var userId = new Guid(Convert.FromBase64String(objectGuidClaim.Value));
context.IssuedClaims.Add(new Claim("UserId", userId.ToString()));
}
return Task.CompletedTask;
}
public Task IsActiveAsync(IsActiveContext context)
{
context.IsActive = true;
return Task.CompletedTask;
}
}
So in my case, without the ProfileService then ObjectGUID is passed through, but using the ProfileService, it's not available in context.Subject.Claims list.
My goal is to take the "ObjectGUID" claim from the idp which is a base64 encoded guid and convert it to a hex string and pass that along as the "UserId" claim from identity server.
I'm not even sure this is the best way. I've also tried converting it through ClaimActions but my action never executes (I tested with a random guid to make sure it wasn't something with the conversion):
o.ClaimActions.MapCustomJson("UserId", obj => {
return Guid.NewGuid().ToString();
});
Is this a better way? Why is it not executing?
Try to:
ensure your Subject does not contain
http://schemas.company.com/identity/claims/objectguid instead of just ObjectGUID
extend your
OpenIdConnect configuration with: o.GetClaimsFromUserInfoEndpoint =
true; together with o.ClaimActions.MapUniqueJsonKey("ObjectGUID", "ObjectGUID"); or o.ClaimActions.MapUniqueJsonKey("http://schemas.company.com/identity/claims/objectguid", "ObjectGUID");
if nothing before helped, try:
o.Events = new OpenIdConnectEvents
{
OnTicketReceived = context =>
{
var identity = context.Principal.Identity as ClaimsIdentity;
StringBuilder builder = new StringBuilder();
var claims = identity?.Claims.Select(x => $"{x.Type}:{x.Value};");
if (claims != null)
builder.AppendJoin(", ", claims);
Logger.LogInformation($"Ticket received: [Claims:{builder}]");
identity?.AddClaim(new Claim("userId", Guid.NewGuid().ToString()));
//you can embed your transformer here if you like
return Task.CompletedTask;
}};
(you can examine the exact incoming ticket here and leave the logging anyway for future purposes)
I am having some issues getting swagger to work with oauth2.
I have created a client in my database like this:
private static void CreateSwaggerClient(DatabaseContext context)
{
var client = new Client
{
ClientId = "swaggerui",
ClientName = "Swagger UI client",
Flow = Flows.Implicit,
Enabled = true,
EnableLocalLogin = true,
AccessTokenType = AccessTokenType.Reference,
AllowAccessTokensViaBrowser = true,
IdentityTokenLifetime = 300,
AccessTokenLifetime = 3600,
AuthorizationCodeLifetime = 300,
AbsoluteRefreshTokenLifetime = 2592000,
SlidingRefreshTokenLifetime = 1296000,
RedirectUris = new List<ClientRedirectUri>
{
new ClientRedirectUri { Uri = "http://localhost:62668/swagger" }
},
AllowedScopes = new List<ClientScope>()
{
new ClientScope
{
Scope = "api"
}
},
ClientSecrets = new List<ClientSecret>()
{
new ClientSecret
{
Value = "secret".Sha256(),
Type = "SharedSecret"
}
}
};
context.Clients.Add(client);
context.SaveChanges();
}
Which has access to my api Scope:
private static void CreateScope(DatabaseContext context)
{
var scope = new Scope
{
Enabled = true,
Name = "api",
DisplayName = "Cormar API",
Description = "Should only be used for trusted internal service side applications",
Required = true,
Emphasize = true,
Type = (int)ScopeType.Resource,
IncludeAllClaimsForUser = false,
ShowInDiscoveryDocument = true,
AllowUnrestrictedIntrospection = true,
ScopeClaims = new List<ScopeClaim>()
{
new ScopeClaim
{
Name = "role",
Description = "Role claim types",
AlwaysIncludeInIdToken = true
},
new ScopeClaim
{
Name = "name",
Description = "The name of the user",
AlwaysIncludeInIdToken = true
},
new ScopeClaim
{
Name ="password",
Description = "Contains the encrypted password for a user",
AlwaysIncludeInIdToken = true
}
},
ScopeSecrets = new List<ScopeSecret>()
{
new ScopeSecret
{
Value = "anothersecret".Sha256(),
Type = "SharedSecret"
}
}
};
context.Scopes.Add(scope);
context.SaveChanges();
}
If I open a browser and navigate to the authorize url like this: https://localhost:44313/identity/connect/authorize?client_id=swaggerui&redirect_uri=http://localhost:62668/swagger&response_type=token&scope=api&state=moo it takes me to a login page, which when I type the username and password brings me to the swagger page with a access_token appended to the URL like this:
#access_token=b49fe5641519c325c17d248d2372d69f&token_type=Bearer&expires_in=3600&scope=api&state=moo
But the issue here is that if I click anything, the access token is removed from the url and if I try any of my endpoints, they all fail with access denied.
I have setup my swagger config like this:
private static void ConfigureSwagger(HttpConfiguration config)
{
config.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "test API");
var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
var commentsFileName = Assembly.GetExecutingAssembly().GetName().Name + ".XML";
var commentsFile = Path.Combine(baseDirectory, "bin", commentsFileName);
c.IncludeXmlComments(commentsFile);
c.OAuth2("oauth2")
.Description("OAuth2 Implicit Grant")
.Flow("implicit")
.AuthorizationUrl("http://localhost:62668/identity/connect/authorize")
.TokenUrl("http://localhost:62668/identity/connect/token")
.Scopes(scopes =>
{
scopes.Add("api", "api access");
});
c.OperationFilter<AssignOAuth2SecurityRequirements>();
}).EnableSwaggerUi(c =>
{
c.EnableOAuth2Support("swaggerui", "secret", "local", "test");
});
}
Can anyone tell me what I am missing?
I managed to get this working.
First of all, my AssignOAuth2SecurityRequirements was setup incorrectly. I actually found the right code here: http://knowyourtoolset.com/2015/08/secure-web-apis-with-swagger-swashbuckle-and-oauth2-part-2/
public class AssignOAuth2SecurityRequirements: IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var actFilters = apiDescription.ActionDescriptor.GetFilterPipeline();
var allowsAnonymous = actFilters.Select(f => f.Instance).OfType<OverrideAuthorizationAttribute>().Any();
if (allowsAnonymous)
return; // must be an anonymous method
//var scopes = apiDescription.ActionDescriptor.GetFilterPipeline()
// .Select(filterInfo => filterInfo.Instance)
// .OfType<AllowAnonymousAttribute>()
// .SelectMany(attr => attr.Roles.Split(','))
// .Distinct();
if (operation.security == null)
operation.security = new List<IDictionary<string, IEnumerable<string>>>();
var oAuthRequirements = new Dictionary<string, IEnumerable<string>>
{
{"oauth2", new List<string> {"api"}}
};
operation.security.Add(oAuthRequirements);
}
}
Next, the redirect_uris for my client were incorrect. They all have to be https and they need the full redirect uri. Mine became this:
new ClientRedirectUri { Uri = "https://localhost:44313/swagger/ui/o2c-html" },
Once these were set up, it all started working.