Is it possible to configure? No mention on the docs page.
I have some unwanted behaviour: my frontend shows you are logged in whereas in fact the token is already expired and backend sends 401 Unauthorized.
See example (upper left corner - credentials and logout button):
This seems like a strange behaviour, if not a bug.
P.S. I used command dotnet new angular -o <output_directory_name> -au Individual
from this ASP.NET Core version.
⚠Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
Related question: is it possible to use refresh tokens here? Or why isn't silent renew working?
UPD: I really thought something like this was already implemented in the example.
@javiercn, sorry to bother, but could you please share your thoughts?
It seems to me that automatic silent renew should indeed be working judging by the line highlighted above (in authorize.service.ts), but it doesn't.
This should work, if not. Its either something is not configured right on the templates or the underlying library has a bug. As far as I understand the library is properly configured.
@javiercn here's a more appropriate example:
Steps to reproduce:
dotnet new angular -o AngularIdentityServer -au Individual
Has this issue been resolved in preview7?
...I am unable to recreate this issue in this preview...
@Svetomechc It seems that the app fails to renew the token as you mentioned.
@Michaelwan777 indeed it does! Hope this gets resolved before ASP.NET Core 3.0 stable release.
Has anyone figured out a work around for this?
@asad-c, nope, I just redirect to login page if that happens.
I encountered the same issue and would like to see if someone could look into this issue. Thanks.
I couldn't wait for this to get resolved so had to create the individual projects myself (Angular client, API and IdentityServer) and it's working as expected.
Care to share any details/pointers how you resolved this?
Care to share any details/pointers how you resolved this?
For me to get this to work was a combination of the IdentityServer quickstart tutorials:
https://identityserver4.readthedocs.io/en/latest/quickstarts/0_overview.html
And the following Pluralsight course:
https://app.pluralsight.com/library/courses/openid-and-oauth2-securing-angular-apps/table-of-contents
The IdentityServer docs give an example of a vanilla JavaScript client, but the Pluralsight course shows how to implement an Angular client.
@asad-c a bit tiresome if you ask me, compared to dotnet new angular -o <output_directory_name> -au Individual
. Also, I thought they'd change Implicit Flow to Authentication Code Flow with 3.0 release, but they haven't. Oh well, I guess this template is deserted. So thanks for sharing your experience, will probably follow through.
@Svetomechc The template uses authorization code + pkce
@javiercn you're right, I looked at the wrong place - forgot that the authorization code was in Microsoft.AspNetCore.ApiAuthorization.IdentityServer package, not in the template itself.
Still, the problem in this issue stands.
@Svetomechc does this happen in production as well?
@berik, no, it currently doesn't happen in production for me. But it's been popping out iframes like crazy, some 20-ish of them.
It does happen in production to me. Svetomechc, how did you configure your production differently other than setting the environment to production?
@caihongxu there are a lot of modifications to my production code (for example I use MongoDB for storing users), so I can't list them all, especially because I have no idea what worked around* token expiration. Sorry.
*though I'm still waiting for the fix proper, as 20+ iframes for authorization slows down my application at startup pretty heavily
In fact, it happens to me as well,
After I deploy a new version, and I relogin, I get 401 error for every request I send using the new token.
For a strange reason, it happens only on production, and not in development mode.
If anyone has succeeded to figure this out, I would really appreciate his help.
@yakeer this happens to me to, by the way, and I haven't found a solution. But I've found a workaround:
authorize.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Router, ActivatedRoute } from '@angular/router';
import { Observable, throwError } from 'rxjs';
import { mergeMap, catchError } from 'rxjs/operators';
import { AuthorizeService } from './authorize.service';
import { AppRoutes } from '../app-routes';
@Injectable({
providedIn: 'root'
})
export class AuthorizeInterceptor implements HttpInterceptor {
constructor(private authorize: AuthorizeService, private router: Router, private activatedRoute: ActivatedRoute) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.authorize.getAccessToken()
.pipe(mergeMap(token => this.processRequestWithToken(token, req, next)));
}
// Checks if there is an access_token available in the authorize service
// and adds it to the request in case it's targeted at the same origin as the
// single page application.
private processRequestWithToken(token: string, req: HttpRequest<any>, next: HttpHandler) {
if (!!token && this.isSameOriginUrl(req)) {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
const pathname = window.location.pathname;
return next.handle(req).pipe(catchError(err => {
if (err instanceof HttpErrorResponse) {
if (err.url.includes('Identity/Account/Login') && !pathname.includes(AppRoutes.authorizationLogin)) {
this.router.navigate([AppRoutes.authorizationLogin], { queryParams: { ReturnUrl: pathname } });
}
}
return throwError(err);
}));
}
private isSameOriginUrl(req: any) {
// It's an absolute url with the same origin.
if (req.url.startsWith(`${window.location.origin}/`)) {
return true;
}
// It's a protocol relative url with the same origin.
// For example: //www.example.com/api/Products
if (req.url.startsWith(`//${window.location.host}/`)) {
return true;
}
// It's a relative url like /api/Products
if (/^\/[^\/].*/.test(req.url)) {
return true;
}
// It's an absolute or protocol relative url that
// doesn't have the same origin.
return false;
}
}
For refresh token not working, are you using a persistent store for Identity Server's operational data? If not, then it's just the in-memory store, which means the refresh tokens would disappear when the process restarts.
https://identityserver4.readthedocs.io/en/latest/reference/ef.html#operational-store-support-for-authorization-grants-consents-and-tokens-refresh-and-reference
@joshpearce of course I am. https://github.com/matteofabbri/AspNetCore.Identity.Mongo in particular. The issue is not specific to the store, though. It remains if I switch over to EF Core and SQLite / SQL Server.
@joshpearce thank you. This solved my issue.
I've added
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly("Infrastructure"));
// Change "Infrastructure" to "typeof(Startup).GetTypeInfo().Assembly.GetName().Name;"
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30; // interval in seconds
})
And whole picture
```
public void ConfigureServices(IServiceCollection services)
{
var connectionString = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext
options.UseSqlServer(connectionString));
services.AddDefaultIdentity
.AddEntityFrameworkStores
services.AddIdentityServer()
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly("Infrastructure"));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30; // interval in seconds
})
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/build"; });
}
@berik are you sure the problem has disappeared after that?
@berik that's what I suspected :c
I have great respect for the work done in this project template, but I feel like anyone considering oidc-client instead of just old fashion session cookies for a web app, even an SPA, needs to think long and hard about whether they really need it. The surface area for subtle auth failure bugs is greatly increased by adding all the client-side logic, and I've never understood the benefits.
Here we're talking about leaving a browser open for hours. You've got background tabs going to sleep and OS's going into power save state working against your auth logic. The user doesn't care about any of this. They just want to either be logged in or out, not partially logged in, wondering how to get the app back working.
Auth is a mess right now. Logging into the Azure Portal sometimes involves four seconds straight of redirects. Then when you log out, they tell you you're probably logged out, but it's best to close the browser to make sure
We write web apps for Radiology, and availability is the number one requirement. I would not introduce oidc-client to our apps.
Moved to master issue: Authentication and authorization for SPAs #16406
Update: I think I addressed this by adding the following in Startup.cs
:
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.TokenValidationParameters.ValidateIssuer = false;
options.TokenValidationParameters.ValidateLifetime = false;
});
Specifically the ValidateLifetime = false
line. My token hasn't expired since then.
Most helpful comment
I have great respect for the work done in this project template, but I feel like anyone considering oidc-client instead of just old fashion session cookies for a web app, even an SPA, needs to think long and hard about whether they really need it. The surface area for subtle auth failure bugs is greatly increased by adding all the client-side logic, and I've never understood the benefits.
Here we're talking about leaving a browser open for hours. You've got background tabs going to sleep and OS's going into power save state working against your auth logic. The user doesn't care about any of this. They just want to either be logged in or out, not partially logged in, wondering how to get the app back working.
Auth is a mess right now. Logging into the Azure Portal sometimes involves four seconds straight of redirects. Then when you log out, they tell you you're probably logged out, but it's best to close the browser to make sure
We write web apps for Radiology, and availability is the number one requirement. I would not introduce oidc-client to our apps.