robisim74 / angularspawebapi Goto Github PK
View Code? Open in Web Editor NEWAngular Single Page Application with an ASP.NET Core Web API that uses token authentication
License: MIT License
Angular Single Page Application with an ASP.NET Core Web API that uses token authentication
License: MIT License
Hey there, this is a great project. I have a similarly set up project and I have a question. I am running an identity server and a web API in the same project using the resource owner password grant. After taking a look at the README this caught my eye: "If more than one client app requires the Web API, use an interactive flow: IdentityServer4 or the other libraries allow you to scale your application"
When I was working on my application scaling was something I was unsure of. Because identity server and the api are in the same application, if I were to set up multiple servers hosting the api, there would also be multiple instances of the identity server application. Do you see any issues with this scenario? This might be a very open-ended question, but this repo is the best example I've come across so far and you seem very knowledgeable about the subject.
Hey,
First of all, thanks for the template,
It really helped me understand things out with IdentityServer and ASPNetCORE + Angular.
I'm facing an issue when trying to deploy to Azure Web App,
I'm getting error 500, and in the Web App logs I see that it cannot find the index.htm, and therefore it throws the error 500.
Did anyone else faced the same issue?
Maybe you've got a solution?
Hi There,
Thanks for your great sample. It has super useful info regarding Angular2-jwt usage with IDSvr.
All the samples I have seen thus far, including work from Damien Bod, ( https://github.com/damienbod/AspNet5IdentityServerAngularImplicitFlow ) has the angular client use implicit flow and redirects to the IdentityServer where the user chooses thier auth type, so they can choose 'local account' or google or whatever is setup on IdSvr.
Is it easy to switch your code from a ROPC flow to Implicit?
I have spent a bit looking at your sample code and that of Damien Bods, however I cannot see what I need to change properly.
In Damiens code the authorize function does a redirect to the IDSvr
public Authorize() {
this.ResetAuthorizationData();
console.log('BEGIN Authorize, no auth data');
let authorizationUrl = this._configuration.server + '/connect/authorize';
let client_id = this._configuration.client_id;
let redirect_uri = this._configuration.redirect_url;
let response_type = this._configuration.response_type;
let scope = this._configuration.scope;
let nonce = 'N' + Math.random() + '' + Date.now();
let state = Date.now() + '' + Math.random();
this.store('authStateControl', state);
this.store('authNonce', nonce);
console.log('AuthorizedController created. adding myautostate: ' + this.retrieve('authStateControl'));
let url =
authorizationUrl + '?' +
'response_type=' + encodeURI(response_type) + '&' +
'client_id=' + encodeURI(client_id) + '&' +
'redirect_uri=' + encodeURI(redirect_uri) + '&' +
'scope=' + encodeURI(scope) + '&' +
'nonce=' + encodeURI(nonce) + '&' +
'state=' + encodeURI(state);
window.location.href = url;
}
So I'd say I'd need to implement the same redirect with your sample as well.
But.... Im just not sure how to handle the redirect using Auth0.
Anyways, you probably have the answer so I'll leave my question at that. Any assistance or answer on this would be greatly appreciated.
Cheers
Hello, thanks for your sample I am able to understand further about such topic. I have a question whether I'm doing it right or wrong.
I am using your sample. However, the difference is the IdentityServer is hosted on its own. The SPA and APIs are hosted on the same server. (Probably separated soon but not now).
Since it's using ROPC, I just get the access tokens with its default claims that I asked for. I may be doing something wrong or missing something, but I tried ProfileService
etc just so to force it to spit out the custom claims when I access connect/token
. However, it doesn't look like I am on the right path and based on my research. It suggested to let the client use connect/userinfo
to get the information. That makes sense. However, my scenario is given the User.Claims
from asp.net, I need to be able to know who you are besides giving me the id such as first name or last name. Currently what I'm doing is the following:
var identityId = User.FindFirst("sub").Value;
value.UserId = new Guid(identityId);
var p = await _userManager.FindByIdAsync(identityId);
var m = await _userManager.GetClaimsAsync(p);
Which works but I'm not sure if it's correct. I wanted to minimize the database calls if the client can just get the claims and pass it through the server. What do you think?
CREATE TABLE [AspNetRoles] (
[Id] TEXT NOT NULL,
[ConcurrencyStamp] TEXT NULL,
[Name] TEXT NULL,
[NormalizedName] TEXT NULL,
CONSTRAINT [PK_AspNetRoles] PRIMARY KEY ([Id])
);: Microsoft.EntityFrameworkCore.Database.Command[200102]
Failed executing DbCommand (9ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE [AspNetRoles] (
[Id] TEXT NOT NULL,
[ConcurrencyStamp] TEXT NULL,
[Name] TEXT NULL,
[NormalizedName] TEXT NULL,
CONSTRAINT [PK_AspNetRoles] PRIMARY KEY ([Id])
);
System.Data.SqlClient.SqlException (0x80131904): Column 'Id' in table 'AspNetRoles' is of a type that is invalid for use as a key column in an index.
Could not create constraint or index. See previous errors.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite, String methodName)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, DbCommandMethod executeMethod, IReadOnlyDictionary`2 parameterValues)
ClientConnectionId:6d7fc6df-3e63-4c32-9b4f-58fc66967739
Error Number:1919,State:1,Class:16
Can you provide fix?
Hi @robisim74 ,
i get this error message when i try to update the database:
"Id column in aspnetroles table is an invalid type for use as key column...."
how can i solve this?
thanks
I can signin successful, get access_token, but do not have refresh_token.
When the site try to get userinfor via connect/userinfo. It always give 405 error
Response for preflight has invalid HTTP status code 405
What're my missing?
thanks
Dear Roberto,
How are you?
I had to perform a little change over your original 'webpack.config.js' file in order to fix a problem which was causing an error when compile in AOT + LazyLoading Modules.
The fix is just to remove the '/app' from the genDir:
'angular-router-loader?aot=true&genDir=aot/app'
to
'angular-router-loader?aot=true&genDir=aot'
Explanation:
When we use a LazyLoaded module: https://angular.io/guide/ngmodule#lazy-loading-modules-with-the-router we need to include its relative path as
{ path: 'values', loadChildren: './values/values.module#ValuesModule' }
and include within the files tag at 'tsconfig-aot.json':
"files": [
"app/app.module.ts",
"app/values/values.module.ts",
"app/main-aot.ts"
],
If we do not perform this fix the AOT routes appears with a duplicate app like '.../app/app/values/values.module...'
I thought you could maybe find it interesting to know in case you include Lazy Loading approach in the future.
Regards,
Andrés.
Hi, Thanks for providing so good example.
I just got a question, when I try the authGard and refresh the page, and if the networking is slow, then the AuthGard will think current user don't have any roles. It will redirect me to the login page.
I think it's because below getUserInfo is async and it didn't return the result before we call the AuthGard?
@Injectable() export class AuthenticationService {
// On bootstrap or refresh, tries to get user's data. if (this.tokenNotExpired()) { this.getUserInfo().subscribe( (userInfo: any) => { this.changeUser(userInfo); }); }
I'm not sure how to fix this, can you please help take a look?
Thanks.
On entering incorrect credentials I get a javascript error, error.json is not a function.
If I change lines 40-42 of signin.ts as follows:
if (error.error) {
switch (error.error.error) {
case 'invalid_grant':
It works as expected. No idea why it's "error.error.error"!
The provided appsettings.json has a hard-coded path in the connection string that won't work on most developer machines. If you modify to just be "Data Source=IdentityDB.sqlite" and specify that this file should be "Copy If Newer" it works out of the box for everyone.
Upgrade to ASP.NET Core 2 when IdentityServer will be ready.
Hi i was trying to "enable-migrations"
i got this error
`Exception calling "SetData" with "2" argument(s): "Type
'Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.Automation.OAProject'
in assembly 'Microsoft.VisualStudio.ProjectSystem.VS.Implementation,
Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' is not marked as
serializable."
At C:\Users\Nour
Ahmed.nuget\packages\entityframework\6.2.0\tools\EntityFramework.psm1:720 char:5
$domain.SetData('project', $project)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Exception calling "SetData" with "2" argument(s): "Type
'Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.Automation.OAProject'
in assembly 'Microsoft.VisualStudio.ProjectSystem.VS.Implementation,
Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' is not marked as
serializable."
At C:\Users\Nour
Ahmed.nuget\packages\entityframework\6.2.0\tools\EntityFramework.psm1:721 char:5
$domain.SetData('contextProject', $contextProject)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Exception calling "SetData" with "2" argument(s): "Type
'Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.Automation.OAProject'
in assembly 'Microsoft.VisualStudio.ProjectSystem.VS.Implementation,
Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' is not marked as
serializable."
At C:\Users\Nour
Ahmed.nuget\packages\entityframework\6.2.0\tools\EntityFramework.psm1:722 char:5
$domain.SetData('startUpProject', $startUpProject)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Data.Entity.Migrations.Extensions.ProjectExtensions.GetPropertyValue[T](Project project, String propertyName)
at System.Data.Entity.Migrations.MigrationsDomainCommand.GetFacade(String configurationTypeName, Boolean useContextWorkingDirectory)
at System.Data.Entity.Migrations.EnableMigrationsCommand.FindContextToEnable(String contextTypeName)
at System.Data.Entity.Migrations.EnableMigrationsCommand.<>c__DisplayClass2.<.ctor>b__0()
at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
Object reference not set to an instance of an object.`
any work around for it
Hello Roberto,
First of all thanks for your contribution! I find its code really great and clean.
I am developing an app which follows your authentication and spa design approach. It needs to perform some custom checks once the user starts the signin proccess in order to know if its user email has not been deleted/blocked from the organization Active Directory in order to let him authorize via /connect/token or not.
Where do you consider I should better place this custom bussiness-logic layer?
My first approach has been to create a new backend API Controller which takes the user signin decision, so I could consume it from the client (signin.ts), before calling the 'this.authenticationService.signin(...)' . So if the API Controller did find the user within the Active Directory and it has not been blocked, the authentication will continue. At the same time, if the users email has been deleted/blocked I will remove it also from the Identity database in order to guarantee he wont be able to access anymore.
The problem is that I find this approach easily hackeable just by using the developer tools. Is there any way I could centralize the sigin token login within the backend to make it more secure?
Thanks really much for your time,
Andrés.
When i try to tun npm start, I got this error:
Loading...
ERROR in ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./app/styles.scss
Module build failed: Error: Missing binding F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\vendor\win32-x64-48\binding.node
Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 6.x
Found bindings for the following environments:
- Windows 64-bit with Node.js 5.x
This usually happens because your environment has changed since running `npm install`.
Run `npm rebuild node-sass --force` to build the binding for your current environment.
at module.exports (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\lib\binding.js:15:13)
at Object.<anonymous> (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\lib\index.js:14:35)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\sass-loader\lib\loader.js:3:14)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
@ ./app/styles.scss 4:14-118 18:2-22:4 19:20-124
@ ./app/main.ts
@ multi webpack-hot-middleware/client?path=%2F__webpack_hmr ./app/main.ts
ERROR in ./app/home/home.component.scss
Module build failed: Error: Missing binding F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\vendor\win32-x64-48\binding.node
Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 6.x
Found bindings for the following environments:
- Windows 64-bit with Node.js 5.x
This usually happens because your environment has changed since running `npm install`.
Run `npm rebuild node-sass --force` to build the binding for your current environment.
at module.exports (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\lib\binding.js:15:13)
at Object.<anonymous> (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\lib\index.js:14:35)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\sass-loader\lib\loader.js:3:14)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
@ ./app/home/home.component.ts 14:21-53
@ ./app/app.module.ts
@ ./app/main.ts
@ multi webpack-hot-middleware/client?path=%2F__webpack_hmr ./app/main.ts
ERROR in ./app/resources/resources.component.scss
Module build failed: Error: Missing binding F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\vendor\win32-x64-48\binding.node
Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 6.x
Found bindings for the following environments:
- Windows 64-bit with Node.js 5.x
This usually happens because your environment has changed since running `npm install`.
Run `npm rebuild node-sass --force` to build the binding for your current environment.
at module.exports (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\lib\binding.js:15:13)
at Object.<anonymous> (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\lib\index.js:14:35)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\sass-loader\lib\loader.js:3:14)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
@ ./app/resources/resources.component.ts 28:21-58
@ ./app/resources/resources.module.ts
@ ./app/app-routing.module.ts
@ ./app/app.module.ts
@ ./app/main.ts
@ multi webpack-hot-middleware/client?path=%2F__webpack_hmr ./app/main.ts
ERROR in ./app/dashboard/dashboard.component.scss
Module build failed: Error: Missing binding F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\vendor\win32-x64-48\binding.node
Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 6.x
Found bindings for the following environments:
- Windows 64-bit with Node.js 5.x
This usually happens because your environment has changed since running `npm install`.
Run `npm rebuild node-sass --force` to build the binding for your current environment.
at module.exports (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\lib\binding.js:15:13)
at Object.<anonymous> (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\node-sass\lib\index.js:14:35)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (F:\lamemmv\study\self-study\asp.net\AngularSPAWebAPI-master\src\AngularSPAWebAPI\node_modules\sass-loader\lib\loader.js:3:14)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
@ ./app/dashboard/dashboard.component.ts 38:21-58
@ ./app/dashboard/dashboard.module.ts
@ ./app/app-routing.module.ts
@ ./app/app.module.ts
@ ./app/main.ts
@ multi webpack-hot-middleware/client?path=%2F__webpack_hmr ./app/main.ts
What're my missing?
Thanks
After logout, If you try to use the previous token, it'll work as if you was still logged in.
It should return 401, right?
Is the logic not implemented yet?
Hi,
I wondered if there is any logic for configuring session time. I would like to make session 20 minutes and logout user, if user didn't show any activity during this 20 minutes. As I understand, I might configure it thru scheduleTokenRefresh() and unscheduleRefresh() with additional logic. Please correct me if I'm wrong.
Best regards,
Taras
This is a great project that really helped me after having looked at many other similar projects.
Would really appreciate if you could add some simple logic for the refresh token and how it fits into this architecture.
https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/
I have switched the code to use an Azure SQL Database and I can register and login just fine however when I login I never seem to get the "given_name" returned. I added the EntityFramework Core SQL driver and can see my accounts getting created correctly in the database but when I watch what happens when I login I only get the GUID, Name, and Role fields returned in the JSON.
This is working perfectly when using the SQLite file that comes with the project.
ERROR in .//css-loader!.//sass-loader!./app/styles/blue-amber.scss
Module build failed: Error: Missing binding C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\node-sass\vendor\win32-x64-51\binding.node
Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 7.x
Found bindings for the following environments:
This usually happens because your environment has changed since running npm install
.
Run npm rebuild node-sass
to build the binding for your current environment.
at module.exports (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\node-sass\lib\binding.js:15:13)
at Object. (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\node-sass\lib\index.js:14:35)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.require (module.js:498:17)
at require (internal/module.js:20:19)
at Object. (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\sass-loader\index.js:4:12)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.require (module.js:498:17)
at require (internal/module.js:20:19)
at loadLoader (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\loadLoader.js:13:17)
at iteratePitchingLoaders (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\LoaderRunner.js:164:2)
at iteratePitchingLoaders (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\LoaderRunner.js:160:10)
at C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\LoaderRunner.js:168:18
at loadLoader (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\loadLoader.js:36:3)
at iteratePitchingLoaders (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\LoaderRunner.js:164:2)
at runLoaders (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\LoaderRunner.js:357:2)
at NormalModule.doBuild (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\NormalModule.js:131:2)
at NormalModule.build (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\NormalModule.js:181:15)
at Compilation.buildModule (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\Compilation.js:129:9)
at factoryCallback (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\Compilation.js:308:10)
at C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\NormalModuleFactory.js:243:4
at C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\NormalModuleFactory.js:94:13
@ ./app/styles/blue-amber.scss 4:14-127
@ ./app/main.ts
ERROR in .//css-loader!.//sass-loader!./app/styles/app.scss
Module build failed: Error: Missing binding C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\node-sass\vendor\win32-x64-51\binding.node
Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 7.x
Found bindings for the following environments:
This usually happens because your environment has changed since running npm install
.
Run npm rebuild node-sass
to build the binding for your current environment.
at module.exports (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\node-sass\lib\binding.js:15:13)
at Object. (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\node-sass\lib\index.js:14:35)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.require (module.js:498:17)
at require (internal/module.js:20:19)
at Object. (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\sass-loader\index.js:4:12)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.require (module.js:498:17)
at require (internal/module.js:20:19)
at loadLoader (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\loadLoader.js:13:17)
at iteratePitchingLoaders (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\LoaderRunner.js:164:2)
at iteratePitchingLoaders (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\LoaderRunner.js:160:10)
at C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\LoaderRunner.js:168:18
at loadLoader (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\loadLoader.js:36:3)
at iteratePitchingLoaders (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\LoaderRunner.js:164:2)
at runLoaders (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\loader-runner\lib\LoaderRunner.js:357:2)
at NormalModule.doBuild (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\NormalModule.js:131:2)
at NormalModule.build (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\NormalModule.js:181:15)
at Compilation.buildModule (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\Compilation.js:129:9)
at factoryCallback (C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\Compilation.js:308:10)
at C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\NormalModuleFactory.js:243:4
at C:\Users\nadav\Desktop\Angular2SPAWebAPI-master\src\Angular2SPAWebAPI\node_modules\webpack\lib\NormalModuleFactory.js:94:13
@ ./app/styles/app.scss 4:14-120
@ ./app/main.ts
Hello Dear,
i'm trying to use role claims , but it doesn't work
first i created role claim by adding these lines of code after created roles
var adminRole = await _roleManager.FindByNameAsync("administrator");
await _roleManager.AddClaimAsync(adminRole, new Claim("my_claim_type", value: "my_claim_value"));
and in services.AddAuthorization i added this line
options.AddPolicy("my_policy", policy => policy.RequireClaim("my_claim_type", "my_claim_value"));
then
[HttpGet]
// [Authorize(Policy = "my_policy")]
[Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme, Policy = "my_policy")]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
please any help to solve this problem
IdentityServer/IdentityServer4#466
Remove the decoding of the access token from the client to ensure that the token has not been altered:
Find a way to set up base urls of configurations (angular-oauth2-oidc & IdentityServer4) depending on your environments:
In both staging & production envs Angular app should build with ng build --prod
Hi,
As per your instructions, I've installed the following:
When I open the project and do a build I get two errors:
Please help me!
Hello, first thank you so much for your effort on this sample project.
I was reviewing it as in my own implementation of Angular+IdentityServer I am having trouble making the Sign Out functionality fully sign out a user and I was hoping this project was doing that successfully but it seems it suffers the same issue.
When you sign in the server returns a Bearer token to use for subsequent api requests say to the value api. When you sign out the app is sending two requests to /connect/revocation one to revoke the access_token and the other to revoke the refresh_token. These both return 200 as to indicate success however someone with the Bearer token can still login after these two revocations have been called.
You can reproduce this by logging in to the app; grab the bearer token with chrome, make a request with that token to the /api/values with something like Postman and include the Bearer token. You will see that even after Log Out you can still successfully use that token meaning it has not actually be revoked.
This is the same issue I am having in my own project and I was wondering if you are aware of this issue and know of any fix to make your Angular2SPAWebAPI successfully Log Out so that tokens can not be reused after the fact? thank you.
Let's see what the Config.cs file contains for configuring IdentityServer4. The following is the identification of our client app:
// Clients want to access resources.
public static IEnumerable<Client> GetClients()
{
// Clients credentials.
return new List<Client>
{
// http://docs.identityserver.io/en/dev/reference/client.html.
new Client
{
ClientId = "Angular2SPA",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, // Resource Owner Password Credential grant.
AllowAccessTokensViaBrowser = true,
RequireClientSecret = false, // This client does not need a secret to request tokens from the token endpoint.
AccessTokenLifetime = 900, // Lifetime of access token in seconds.
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId, // For UserInfo endpoint.
IdentityServerConstants.StandardScopes.Profile,
"WebAPI",
"roles"
},
AllowOfflineAccess = true // For refresh token.
}
};
}
As you can see, you can add other clients with their own configuration.
Our Angular 2 app, identified as Angular2SPA:
// Identity resources (used by UserInfo endpoint).
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResource("roles", new List<string> { "role" })
};
}
// Api resources.
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("WebAPI" ) {
UserClaims = { "role" }
}
};
}
Note that we can define which user claims will be included in the access token.
Because our Web API is in the same project, in Configure method of Startup.cs file,
we add the authentication middleware:
// IdentityServer4.AccessTokenValidation: authentication middleware for the API.
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000/",
AllowedScopes = { "WebAPI" },
RequireHttpsMetadata = false
});
We complete the configuration by adding IdentityServer in ConfigureServices method:
// Adds IdentityServer.
// The AddTemporarySigningCredential extension creates temporary key material for signing tokens on every start.
// Again this might be useful to get started, but needs to be replaced by some persistent key material for production scenarios.
// See the cryptography docs for more information: http://docs.identityserver.io/en/release/topics/crypto.html#refcrypto
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>(); // IdentityServer4.AspNetIdentity.
The extension method AddAspNetIdentity to use the ASP.NET Identity requires another setting:
// Adds IdentityServer.
app.UseIdentityServer();
Now we can add related services: Identity and for simplicity SQLite.
// Identity & SQLite.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
and
// Adds Identity.
app.UseIdentity();
Also we define the policy of access to the Web Api controllers.
In our sample, we create two policies:
// Claims-Based Authorization: role claims.
services.AddAuthorization(options =>
{
// Policy for dashboard: only administrator role.
options.AddPolicy("Manage Accounts", policy => policy.RequireClaim("role", "administrator"));
// Policy for resources: user or administrator role.
options.AddPolicy("Access Resources", policyBuilder => policyBuilder.RequireAssertion(
context => context.User.HasClaim(claim => (claim.Type == "role" && claim.Value == "user")
|| (claim.Type == "role" && claim.Value == "administrator"))
)
);
});
We add the authorization to the Identity controller, which is used by the dashboard:
[Route("api/[controller]")]
[Authorize(Policy = "Manage Accounts")] // Authorization policy for this API.
public class IdentityController : Controller
...
and to Values controller, that returns the resources for the authenticated users:
[Route("api/[controller]")]
[Authorize(Policy = "Access Resources")] // Authorization policy for this API.
public class ValuesController : Controller
...
Remember: when we have defined our APIResource, we have included the roles with user claim role to allow the user to access the resources.
Finally, we set the startup on the entry point of the client application:
// Microsoft.AspNetCore.StaticFiles: API for starting the application from wwwroot.
// Uses default files as index.html.
app.UseDefaultFiles();
// Uses static file for the current path.
app.UseStaticFiles();
It's important to understand and observe how the authentication and authorization services work,
in order to implement the client app.
If you debug the app and navigate the browers to:
http://localhost:5000/.well-known/openid-configuration
you should see the so-called discovery document.
The discovery endpoint can be used to retrieve metadata about IdentityServer.
For an authentication, we need to send the request at the token_endpoint
:
http://localhost:5000/connect/token
For example, you can use Postman as client to send this POST request:
POST /connect/token HTTP/1.1
Host: localhost:5000
Content-Type: application/x-www-form-urlencoded
client_id=Angular2SPA&grant_type=password&username=admin%40gmail.com&password=Admin01*&scope=WebAPI+offline_access+openid+profile+roles
Note the Content-Type as x-www-form-urlencoded, and parameters provided in the body. This is the response:
{
"access_token": "eyJhbGci...",
"expires_in": 900,
"token_type": "Bearer",
"refresh_token": "5007bc4b..."
}
The user has been authenticated, and he has an access token that will expire in 900 seconds, but he has also a refresh token.
You can use a tool like JSON Web Token to decode the JWT and see the payload (with our scope claims):
{
"nbf": 1480712377,
"exp": 1480713277,
"iss": "http://localhost:5000",
"aud": "http://localhost:5000/resources",
"client_id": "Angular2SPA",
"sub": "c0bb2220-8c99-46dc-ad39-b707f37f047f",
"auth_time": 1480712377,
"idp": "local",
"role": "administrator",
"scope": [
"offline_access",
"openid",
"profile",
"roles",
"WebAPI"
],
"amr": [
"pwd"
]
}
Now we can send a GET request to our Web API in this way:
GET /api/values HTTP/1.1
Host: localhost:5000
Authorization: Bearer eyJhbGci...
This request contains a header parameter named Authorization and its value is the bearer token. The response:
[
"value1",
"value2"
]
And when, past the 15 minutes, the token expires? The user can no longer access resources.
You can ask him to sign in again, or handle a refresh token strategy: to get a new access token,
you can send a POST request, with grant_type
set to refresh_token
and refresh token
as parameters:
POST /connect/token HTTP/1.1
Host: localhost:5000
Content-Type: application/x-www-form-urlencoded
client_id=Angular2SPA&grant_type=refresh_token&refresh_token=5007bc4b...
Ok, how do we transform the requests done via Postman in an Angular 2 app?
In this sample, to send unauthenticated requests for signing in and signing up the user, we use the Angular 2 http module,
as in AuthenticationService class:
/**
* Tries to sign in the user.
*
* @param username
* @param password
* @return The user's data
*/
public signin(username: string, password: string): Observable<any> {
// Token endpoint & params.
let tokenEndpoint: string = Config.TOKEN_ENDPOINT;
let params: any = {
client_id: Config.CLIENT_ID,
grant_type: Config.GRANT_TYPE,
username: username,
password: password,
scope: Config.SCOPE
};
// Encodes the parameters.
let body: string = this.encodeParams(params);
this.authTime = new Date().valueOf();
return this.http.post(tokenEndpoint, body, this.options)
.map((res: Response) => {
let body: any = res.json();
// Sign in successful if there's an access token in the response.
if (typeof body.access_token !== 'undefined') {
// Stores access token & refresh token.
this.store(body);
// Gets user info.
this.userInfo();
// Tells all the subscribers about the new status.
this.signinSubject.next(true);
}
}).catch((error: any) => {
// Error on post request.
return Observable.throw(error);
});
}
We send a request to UserInfo endpoint to get the user's data
using angular2-jwt library, that builds for us the header with the authorization token:
/**
* Calls UserInfo endpoint to retrieve user's data.
*/
private userInfo(): void {
if (this.tokenNotExpired()) {
this.authHttp.get(Config.USERINFO_ENDPOINT)
.subscribe(
(res: any) => {
let user: any = res.json();
let roles: string[] = user.role;
// Tells all the subscribers about the new data & roles.
this.userSubject.next(user);
this.rolesSubject.next(user.role);
},
(error: any) => {
// Need to handle this error.
console.log(error);
});
}
}
In this example, we use a scheduler to request a new access token before it expires through the refresh token:
/**
* Optional strategy for refresh token through a scheduler.
*
* Will schedule a refresh at the appropriate time.
*/
public scheduleRefresh(): void {
let source = this.authHttp.tokenStream.flatMap(
(token: string) => {
let delay: number = this.expiresIn - this.offsetSeconds * 1000;
return Observable.interval(delay);
});
this.refreshSubscription = source.subscribe(() => {
this.getNewToken().subscribe(
() => {
// Scheduler works.
},
(error: any) => {
// Need to handle this error.
console.log(error);
}
);
});
}
/**
* Case when the user comes back to the app after closing it.
*/
public startupTokenRefresh(): void {
// If the user is authenticated, uses the token stream
// provided by angular2-jwt and flatMap the token.
if (this.signinSubject.getValue()) {
let source = this.authHttp.tokenStream.flatMap(
(token: string) => {
let now: number = new Date().valueOf();
let exp: number = Helpers.getExp();
let delay: number = exp - now - this.offsetSeconds * 1000;
// Uses the delay in a timer to run the refresh at the proper time.
return Observable.timer(delay);
});
// Once the delay time from above is reached, gets a new JWT and schedules additional refreshes.
source.subscribe(() => {
this.getNewToken().subscribe(
() => {
this.scheduleRefresh();
},
(error: any) => {
// Need to handle this error.
console.log(error);
}
);
});
}
}
/**
* Unsubscribes from the scheduling of the refresh token.
*/
public unscheduleRefresh(): void {
if (this.refreshSubscription) {
this.refreshSubscription.unsubscribe();
}
}
/**
* Tries to get a new token using refresh token.
*/
public getNewToken(): Observable<any> {
let refreshToken: string = Helpers.getToken('refresh_token');
// Token endpoint & params.
let tokenEndpoint: string = Config.TOKEN_ENDPOINT;
let params: any = {
client_id: Config.CLIENT_ID,
grant_type: "refresh_token",
refresh_token: refreshToken
};
// Encodes the parameters.
let body: string = this.encodeParams(params);
this.authTime = new Date().valueOf();
return this.http.post(tokenEndpoint, body, this.options)
.map((res: Response) => {
let body: any = res.json();
// Successful if there's an access token in the response.
if (typeof body.access_token !== 'undefined') {
// Stores access token & refresh token.
this.store(body);
}
}).catch((error: any) => {
// Error on post request.
return Observable.throw(error);
});
}
To send authenticated requests, as in ResourcesComponent class, we still use angular2-jwt library:
// Sends an authenticated request.
this.authHttp.get("/api/values")
.subscribe(
(res: any) => {
this.values = res.json();
},
(error: any) => {
console.log(error);
});
For production, we build the Angular 2 app through ngc compiler & webpack.
To do this, after the AoT compilation, in webpack.config.js file we set as entry point main-aot.ts:
// In production mode, we use AoT compilation & minification.
module.exports = {
entry: {
'app-aot': './app/main-aot.js'
},
...
and as output the wwwroot folder (as set in Startup.cs):
output: {
path: "./wwwroot/",
filename: "dist/[name].bundle.js",
chunkFilename: 'dist/[name].chunk.js'
},
Finally, we ask webpack to insert the script of the bundle in our index.html:
// Adds script for the bundle in index.html.
new HtmlWebpackPlugin({
filename: 'index.html',
inject: 'body',
template: 'app/index.html'
})
Switch to HttpClient
when angular-jwt will be ready: https://github.com/auth0/angular2-jwt/tree/v1.0
Hello yor example is very good and clear.
i see that you register manually and in memory the client called AngularSPA, in Config.cs file.
.......
// Clients credentials.
return new List
{
// http://docs.identityserver.io/en/release/reference/client.html.
new Client
{
ClientId = "AngularSPA",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, // Resource Owner Password Credential grant.
AllowAccessTokensViaBrowser = true,
RequireClientSecret = false, // This client does not need a secret to request tokens from the token endpoint.
AccessTokenLifetime = 900, // Lifetime of access token in seconds.
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId, // For UserInfo endpoint.
IdentityServerConstants.StandardScopes.Profile,
"roles",
"WebAPI"
},
AllowOfflineAccess = true, // For refresh token.
RefreshTokenUsage = TokenUsage.OneTimeOnly,
AbsoluteRefreshTokenLifetime = 7200,
SlidingRefreshTokenLifetime = 900,
RefreshTokenExpiration = TokenExpiration.Sliding
}
};
.......
if i need to register other client and then save in database?
other question is is how get a current user in a controller?
Thank so much.
Thank you for great sample! It is interesting to know how it is possible to implement refresh_token to use short-lived access tokens and update them using refresh_tokens, that can be revoked
Hi I am looking at using this SPA template as it's a great start. I have tried to integrate swagger using the following article
https://docs.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger
but dont have any luck as an empty swagger doc is shown, any ideas ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.