Popular New Releases in Authorization
casbin
v2.44.2
laravel-permission
5.5.2
RxPermissions
v0.12
opa
v0.39.0
Dexter
Fix duplicated permission report
Popular Libraries in Authorization
by casbin go
11753 Apache-2.0
An authorization library that supports access control models like ACL, RBAC, ABAC in Golang
by spatie php
10248 MIT
Associate users with roles and permissions
by tbruyelle java
10169 Apache-2.0
Android runtime permissions powered by RxJava2
by open-policy-agent go
6500 Apache-2.0
An open source, general-purpose policy engine.
by ryanb ruby
6313 MIT
Authorization Gem for Ruby on Rails.
by Zizaco php
6180 MIT
Role-based Permissions for Laravel 5
by sebastianbergmann php
6115 NOASSERTION
Snapshotting of global state, factored out of PHPUnit into a stand-alone component
by Karumi java
4982 Apache-2.0
Android library that simplifies the process of requesting permissions at runtime.
by nickoneill swift
4909 MIT
Intelligent iOS permissions UI and unified API
Trending New libraries in Authorization
by osohq rust
2362 Apache-2.0
Oso is a batteries-included framework for building authorization in your application.
by ovh perl
1052 NOASSERTION
Authentication, authorization, traceability and auditability for SSH accesses.
by cerbos go
341 Apache-2.0
Cerbos is the open core, language-agnostic, scalable authorization solution that makes user permissions and authorization simple to implement and manage by writing context-aware access control policies for your application resources.
by JonPSmith csharp
257 MIT
This library provides extra authorization and multi-tenant features to an ASP.NET Core application.
by greenpau go
227 Apache-2.0
Authorization Plugin for Caddy v2 (JWT/PASETO)
by kubernetes-sigs go
209 Apache-2.0
Home of the Hierarchical Namespace Controller (HNC). Adds hierarchical policies and delegated creation to Kubernetes namespaces for improved in-cluster multitenancy.
by seknox go
201 MPL-2.0
Zero Trust Service Access
by greenpau go
189 Apache-2.0
JWT Authorization Plugin for Caddy v2
by casbin c++
168 Apache-2.0
An authorization library that supports access control models like ACL, RBAC, ABAC in C/C++
Top Authors in Authorization
1
41 Libraries
20625
2
19 Libraries
1645
3
9 Libraries
241
4
8 Libraries
66
5
6 Libraries
9383
6
6 Libraries
2424
7
6 Libraries
205
8
5 Libraries
20
9
5 Libraries
614
10
5 Libraries
434
1
41 Libraries
20625
2
19 Libraries
1645
3
9 Libraries
241
4
8 Libraries
66
5
6 Libraries
9383
6
6 Libraries
2424
7
6 Libraries
205
8
5 Libraries
20
9
5 Libraries
614
10
5 Libraries
434
Trending Kits in Authorization
No Trending Kits are available at this moment for Authorization
Trending Discussions on Authorization
Instead change the require of index.js, to a dynamic import() which is available in all CommonJS modules
Google Colab - Google Drive can´t be mounted anymore - Browser Popup (Google Drive for Desktop) instead of Link in the code output for authorization
Google OAuth 2.0 failing with Error 400: invalid_request for some client_id, but works well for others in the same project
Pushing from Eclipse to my GitHub repository via HTTPS stopped working: "git-receive-pack not permitted" error
How to use scoped APIs with (GSI) Google Identity Services
Any POST or GET requests from the Revue API return 401
Android Studio Disconnects From Physical Device
throwError(error) is now deprecated, but there is no new Error(HttpErrorResponse)
Google Colab drive mount (with underscore) is not working
Save authenticated users to database coming from Azure AD
QUESTION
Instead change the require of index.js, to a dynamic import() which is available in all CommonJS modules
Asked 2022-Apr-05 at 06:25Trying to work with node/javascript/nfts, I am a noob and followed along a tutorial, but I get this error:
1error [ERR_REQUIRE_ESM]: require() of ES Module [...] is not supported. Instead change the require of index.js [ in my file...] to a dynamic import() which is available in all CommonJS modules
2
My understanding is that they've updated the node file, so i need a different code than that in the tutorial, but i don't know which one I'm supposed to change, where and to what. Please be as specific as you can
1error [ERR_REQUIRE_ESM]: require() of ES Module [...] is not supported. Instead change the require of index.js [ in my file...] to a dynamic import() which is available in all CommonJS modules
2const FormData = require('form-data');
3const fetch = require('node-fetch');
4const path = require("path")
5const basePath = process.cwd();
6const fs = require("fs");
7
8fs.readdirSync(`${basePath}/build/images`).foreach(file).forEach(file => {
9 const formData = new FormData();
10 const fileStream = fs.createReadStream(`${basePath}/build/images/${file}`);
11 formData.append('file',fileStream);
12
13 let url = 'https://api.nftport.xyz/v0/files';
14
15 let options = {
16 method: 'POST',
17 headers: {
18 Authorization: '[...]',
19 },
20 body: formData
21 };
22
23 fetch(url, options)
24 .then(res => res.json())
25 .then(json => {
26 const fileName = path.parse(json.file_name).name;
27 let rawdata = fs.readFileSync(`${basePath}/build/json/${fileName}.json`);
28 let metaData = JSON.parse(rawdata);
29
30 metaData.file_url = json.ipfs_url;
31
32 fs.writeFileSync(`${basePath}/build/json${fileName}.json`, JSON.stringify(metaData, null, 2));
33
34 console.log(`${json.file_name} uploaded & ${fileName}.json updated!`);
35 })
36 .catch(err => console.error('error:' + err));
37})
38
39
ANSWER
Answered 2021-Dec-31 at 10:07It is because of the node-fetch
package. As recent versions of this package only support ESM, you have to downgrade it to an older version node-fetch@2.6.1
or lower.
npm i node-fetch@2.6.1
This should solve the issue.
QUESTION
Google Colab - Google Drive can´t be mounted anymore - Browser Popup (Google Drive for Desktop) instead of Link in the code output for authorization
Asked 2022-Apr-01 at 09:48Since yesterday I have had the problem that I can no longer mount my Google account. Normally, when I run it, I get a link to authorize myself with. Now, when the code is executed, an extra browser window is opened where I should authorize myself. But if I do it over it, it doesn't work. Do you know why it can be that this authorization link is suddenly no longer shown? Any security setting maybe? I've tried several browsers.
EDIT: With the new authorization popup it works if i mount the google drive from the same google account like colab. But the problem is that my main google drive is on another account than Google Colab. With the link it used to work without any problems earlier...
EDIT 2: I have now solved it in such a way that I have shared the required folder for my other account and can now access it via my Colab Google Drive account. But I still didn't manage to get the link back.
After the code execution and authorization with the new popup i get this error message on Google Colab:
MessageError Traceback (most recent call last) in () 1 #Connect Google Drive 2 from google.colab import drive ----> 3 drive.mount('/gdrive')
3 frames /usr/local/lib/python3.7/dist-packages/google/colab/_message.py in read_reply_from_input(message_id, timeout_sec) 104 reply.get('colab_msg_id') == message_id): 105 if 'error' in reply: --> 106 raise MessageError(reply['error']) 107 return reply.get('data', None) 108
MessageError: Error: credential propagation was unsuccessful
I use this code:
1#Connect Google Drive
2from google.colab import drive
3drive.mount('/gdrive')
4
ANSWER
Answered 2021-Nov-07 at 20:45This is a problem with Google Colab Pro. I have a Pro account as well as a normal account. My normal account works as intended (with the link) whereas my Pro account has the pop-up window that gives me the same error as OP.
QUESTION
Google OAuth 2.0 failing with Error 400: invalid_request for some client_id, but works well for others in the same project
Asked 2022-Mar-30 at 14:21We have some apps (or maybe we should call them a handful of scripts) that use Google APIs to facilitate some administrative tasks. Recently, after making another client_id in the same project, I started getting an error message similar to the one described in localhost redirect_uri does not work for Google Oauth2 (results in 400: invalid_request error). I.e.,
Error 400: invalid_request
You can't sign in to this app because it doesn't comply with Google's OAuth 2.0 policy for keeping apps secure.
You can let the app developer know that this app doesn't comply with one or more Google validation rules.
Request details:
The content in this section has been provided by the app developer. This content has not been reviewed or verified by Google.
If you’re the app developer, make sure that these request details comply with Google policies.
redirect_uri: urn:ietf:wg:oauth:2.0:oob
How do I get through this error? It is important to note that:
- The OAuth consent screen for this project is marked as "Internal". Therefore any mentions of Google review of the project, or publishing status are irrelevant
- I do have "Trust internal, domain-owned apps" enabled for the domain
- Another client id in the same project works and there are no obvious differences between the client IDs - they are both "Desktop" type which only gives me a Client ID and Client secret that are different
- This is a command line script, so I use the "copy/paste" verification method as documented here hence the
urn:ietf:wg:oauth:2.0:oob
redirect URI (copy/paste is the only friendly way to run this on a headless machine which has no browser). - I was able to reproduce the same problem in a dev domain. I have three client ids. The oldest one is from January 2021, another one from December 2021, and one I created today - March 2022. Of those, only the December 2021 works and lets me choose which account to authenticate with before it either accepts it or rejects it with "Error 403: org_internal" (this is expected). The other two give me an "Error 400: invalid_request" and do not even let me choose the "internal" account. Here are the URLs generated by my app (I use the ruby google client APIs) and the only difference between them is the client_id - January 2021, December 2021, March 2022.
Here is the part of the code around the authorization flow, and the URLs for the different client IDs are what was produced on the $stderr.puts url
line. It is pretty much the same thing as documented in the official example here (version as of this writing).
1
2OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
3
4def user_credentials_for(scope, user_id = 'default')
5 token_store = Google::Auth::Stores::FileTokenStore.new(:file => token_store_path)
6 authorizer = Google::Auth::UserAuthorizer.new(client_id, scope, token_store)
7 credentials = authorizer.get_credentials(user_id)
8 if credentials.nil?
9 url = authorizer.get_authorization_url(base_url: OOB_URI)
10 $stderr.puts ""
11 $stderr.puts "-----------------------------------------------"
12 $stderr.puts "Requesting authorization for '#{user_id}'"
13 $stderr.puts "Open the following URL in your browser and authorize the application."
14 $stderr.puts url
15 code = $stdin.readline.chomp
16 $stderr.puts "-----------------------------------------------"
17 credentials = authorizer.get_and_store_credentials_from_code(
18 user_id: user_id, code: code, base_url: OOB_URI)
19 end
20 credentials
21end
22
23
ANSWER
Answered 2022-Mar-02 at 07:56steps.oauth.v2.invalid_request 400 This error name is used for multiple different kinds of errors, typically for missing or incorrect parameters sent in the request. If is set to false, use fault variables (described below) to retrieve details about the error, such as the fault name and cause.
- GenerateAccessToken GenerateAuthorizationCode
- GenerateAccessTokenImplicitGrant
- RefreshAccessToken
QUESTION
Pushing from Eclipse to my GitHub repository via HTTPS stopped working: "git-receive-pack not permitted" error
Asked 2022-Mar-25 at 03:18I recently did a push to my GitHub repository for a few weeks ago. I got a main from GitHub that GitHub is soon quitting regular authorization and going to replace it with another authorization method.
So today I push a new update to my GitHub repository and got the message:
1git-receive-pack not permitted
2
That's leads to two questions:
- Has EGit stopped working now?
- I have Eclipse 2021-03, how can I fix this issue so I can do a push?
ANSWER
Answered 2021-Aug-20 at 07:52Since August 13, 2021, GitHub does not support authentication via HTTPS with your GitHub account password for security reasons anymore. Instead, in Eclipse, when pushing to a GitHub repository or when fetching from a private repository, you will get a git-upload-pack not permitted on 'https://github.com...'
error.
As solution, use either
- a GitHub specific Personal access tokens as password instead of your previously used GitHub account password or
- SSH with an SSH key of which the private and public key is on your local machine and configured in Eclipse and the public key is uploaded to your GitHub account instead.
- Go to your GitHub account to Settings > Developer settings > Personal access tokens website:
- Click the Generate new token button in the upper right
- Enter a Note, e.g.
GitHub repo token
- Choose Expiration, e.g. No expiration
- Tick the checkbox repo
- Enter a Note, e.g.
- Click the Generate token button at the bottom
- Copy the generated token to the clipboard
- Click the Generate new token button in the upper right
- In Eclipse, in the Git Repositories view:
- Right-click the Remotes sub-node for GitHub (
origin
or the name you have chosen when you have cloned the repository) and choose Configure Push... - Click the Change... button to change the URI in the upper right
- Replace the password with with the copied generated GitHub token
- Click Finish and Save to apply the changes
- Right-click the Remotes sub-node for GitHub (
- Create an SSH key (skip this step when you already have one):
- In Eclipse, in the preferences General > Network Connections > SSH2 tab Key Management hit the Generate RSA Key... button
- Hit Save Private Key... and choose a location, preferably the subfolder
.ssh
of your user home directory
- Upload public key to your GitHub account:
- For a new created key, copy the string shown in the Key Management tab to the clipboard; for an existing key add it in the preferences General > Network Connections > SSH2 tab General and copy the content of the public key file
<name>.pub
- Go to your GitHub account settings to the SSH and GPG keys section and hit the New SSH key button
- Paste the copied public key into the Key field
- For a new created key, copy the string shown in the Key Management tab to the clipboard; for an existing key add it in the preferences General > Network Connections > SSH2 tab General and copy the content of the public key file
- Change HTTPS to SSH URLs of already cloned repositories:
- In Eclipse, in the Git Repositories view right-click the repository and choose Properties and click the Open button
- In the text editor of the
config
file change the remote URL as follows:
HTTPS (old; does not work for push anymore):
1git-receive-pack not permitted
2url = <b>https</b><b>://</b>github.com<b>/</b><username>/<repo>.git
SSH (new):
1git-receive-pack not permitted
2url = <b>https</b><b>://</b>github.com<b>/</b><username>/<repo>.giturl = <b>git@</b>github.com<b>:</b><username>/<repo>.git
QUESTION
How to use scoped APIs with (GSI) Google Identity Services
Asked 2022-Mar-17 at 00:31Google recently sent me an email with the following:
One or more of your web applications uses the legacy Google Sign-In JavaScript library. Please migrate your project(s) to the new Google Identity Services SDK before March 31, 2023
The project in question uses the Google Drive API alongside the now legacy authentication client.
The table on the migration page (https://developers.google.com/identity/gsi/web/guides/migration) says:
Old | New | Notes |
---|---|---|
JavaScript libraries | ||
apis.google.com/js/platform.js | accounts.google.com/gsi/client | Replace old with new. |
apis.google.com/js/api.js | accounts.google.com/gsi/client | Replace old with new. |
I was currently using gapi
on the front-end to perform authorization which is loaded from apis.google.com/js/api.js
. According to the table I would need to replace it with the new library.
I've tried the following to authenticate and authorize in the same manner that I used to do with gapi:
1window.google.accounts.id.initialize({
2 client_id: GOOGLE_CLIENT_ID,
3 callback: console.log,
4 scope: "https://www.googleapis.com/auth/drive.file",
5 discoveryDocs: ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"],
6});
7
8window.google.accounts.id.renderButton(ref.current, {
9 size: "medium",
10 type: "standard",
11});
12
13
However, when I try to authenticate with the Google Sign In button, the scope
field is not respected and it does not ask the user to authorize the requested scopes. It also doesn't return any form of access token in the Credential Response in the callback.
I'm not sure how else to authorize using the new library.
ANSWER
Answered 2021-Aug-26 at 19:19In the new Gooogle Identity Services, the authentication moment and the authorization moment are separated. This means, GIS provides different APIs for websites to call on these two different moments. You cannot combine them together in one API call (and UX flow) any more.
In the authenction moment, users just sign in or sign up into your website (by leveraging the information shared by Google). The only decision users need to make is whether they want to sign in (or sign-up). No authorization-related decison need to make at this point.
In the authentication moment, users will see consistent One Tap or button UX across all websites (since the same scopes are requested implicitly). Consistence leads to more smoothly UX, which may further lead to more usage. With the consitent and optimized authentication UX (across all websites), users will have a better experience with federated sign-in.
After users sign-in, when you really want to load some data from a Google data service, you can call GIS authorization API to trigger an UX flow to allow end users to grant the permission. That's the authorization moment.
Currently (August 2021), only authentication API has been published. If your website only cares about authentication, you can migrate to GIS now. If you also need the authorization API, you have to wait for further notice.
QUESTION
Any POST or GET requests from the Revue API return 401
Asked 2022-Mar-08 at 13:55I am trying to add subscribers to my newsletter using the Revue api. According to the documentation, I need to add a header called 'Authorization' and value 'Token MY-TOKEN' in my requests.
In order to test out the API I am using Postman as seen in the screenshot below:
Any request I do to any url, ends up with a 401.
What am I missing here? The token value is copy pasted from the bottom of https://www.getrevue.co/app/integrations ('Your API key is xyz') as the documentation mentions. Double checked that there are no extra spaces added.
ANSWER
Answered 2022-Jan-06 at 07:43If you have the following when you log in to Revue
"We are reviewing your account."
You will not be able to make API calls and will get a 401.
I've talked to support on the issue and unfortunately, it's undocumented at the moment.
Took nearly a week for me to get reviewed but it's working fine now. It is at the end of the Christmas period so I am hoping they are only temporarily that slow at reviewing accounts.
QUESTION
Android Studio Disconnects From Physical Device
Asked 2022-Mar-06 at 15:11Android Studio Bumblebee (2021.1.1) was released stably on 25 January 2022 bundled with a new Device Manager (accompanying new support for Android 11+ device debugging over WIFI). I jumped on this stable release, updating from Android Studio Arctic Fox (2020.3.1 Patch 4).
Unfortunately however, since updating, physical devices/handsets don't remain connected to Android Studio for the purpose of debugging. I can confirm that the issue was introduced from Android Studio Bumblebee onwards (occurring in Beta and Canary builds also). I've reproduced the issue on Android Studio Bumblebee (Stable), Chipmunk (Beta), and Dolphin (Canary), but Android Studio Arctic Fox (superseded Stable) continues to work just fine.
The issue occurs soon after opening Android Studio (Bumblebee+) with one of my physical devices connected. Everything appears fine initially and I may even have enough time to deploy my project to the handset, before the device disappears from Android Studio (as if I'd physically disconnected the USB cable from my computer or from the handset itself).
I've tried a fair few things in an attempt to determine a root cause. These include testing:
- With different USB cables.
- With different handsets (both varying makes and models).
- With various versions of the Android Studio IDE (as mentioned above).
- Plugging the USB cables into different USB ports on my computer.
- Rebooting handsets and my computer.
- Restarting Android Studio.
- Invalidating caches and restarting Android Studio.
adb kill-server
thenadb start-server
.- Revoking/reaccepting USB debugging authorization.
- Reinstalled build tools/platform tools, and ADB.
- A great number of further possibilities, to no avail.
I searched and read through remotely similar issues, including (but not limited to) these:
- Android Studio Arctic Fox (Adb) - Connected Devices are being disconnected after some time
- Android debugger continually disconnects
This particular comment in one of the above issues clued me onto a possible root cause:
I have been fighting for a few days with adb not seeing my device. After trying many other posted solutions, I discovered that the issue was with Chrome also trying to connect its debugger to a web view. If Chrome is connected using chrome://inspect, then adb seems to disconnect. Quitting Chrome resolves the issue. Then I can connect with Android Studio and then restart Chrome and reconnect. Hope this helps someone else.
However I've been unable to do anything with the above discovery, other than close Google Chrome, and hope for the best. Obviously this isn't an ideal solution. It appears as though the moment Google Chrome shows the connected physical device in the chrome://inspect/#devices page, the physical device promptly becomes unavailable through Android Studio.
I've jumped back to Android Studio Arctic Fox (2020.3.1 Patch 4) for the moment, however this brings with it other issues (my current core project targets the latest SDK version, which requires the updated IDE).
Absolutely any help with this would be insanely appreciated. I've exhausted just about every avenue that I can think of!
ANSWER
Answered 2022-Feb-01 at 17:29I solved the problem by disabling
Settings -> Build, Execution, Deployment -> Debugger -> "Enable adb mDNS for wireless debugging"
QUESTION
throwError(error) is now deprecated, but there is no new Error(HttpErrorResponse)
Asked 2022-Mar-01 at 00:42Apparently throwError(error)
is now deprecated. The IntelliSense of VS Code suggests throwError(() => new Error('error')
. new Error(...)
accepts only strings. What's the correct way to replace it without breaking my HttpErrorHandlerService
?
1import { Injectable } from '@angular/core';
2import {
3 HttpEvent,
4 HttpInterceptor,
5 HttpHandler,
6 HttpRequest,
7 HttpErrorResponse,
8 HttpResponse,
9 HttpHeaders
10} from '@angular/common/http';
11import { Observable, EMPTY, finalize, catchError, timeout, map, throwError } from 'rxjs';
12
13import { HttpErrorHandlerService } from '@core/services';
14
15@Injectable()
16export class HttpErrorInterceptor implements HttpInterceptor {
17 private readonly APP_XHR_TIMEOUT = 6000;
18
19 constructor(private errorHandlerService: HttpErrorHandlerService) {}
20
21 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
22 return next.handle(this.performRequest(request)).pipe(
23 timeout(this.APP_XHR_TIMEOUT),
24 map((event: HttpEvent<any>) => this.handleSuccessfulResponse(event)),
25 catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next)),
26 finalize(this.handleRequestCompleted.bind(this))
27 );
28 }
29
30 private performRequest(request: HttpRequest<any>): HttpRequest<any> {
31 let headers: HttpHeaders = request.headers;
32 //headers = headers.set('MyCustomHeaderKey', `MyCustomHeaderValue`);
33 return request.clone({ headers });
34 }
35
36 private handleSuccessfulResponse(event: HttpEvent<any>): HttpEvent<any> {
37 if (event instanceof HttpResponse) {
38 event = event.clone({ body: event.body.response });
39 }
40 return event;
41 }
42
43 private processRequestError(
44 error: HttpErrorResponse,
45 request: HttpRequest<any>,
46 next: HttpHandler
47 ): Observable<HttpEvent<any>> {
48 console.log('http error response');
49
50 if ([401].includes(error.status)) {
51 return throwError(error);
52 }
53
54 this.errorHandlerService.handle(error);
55
56 return throwError(error);
57 }
58
59 private handleRequestCompleted(): void {
60 // console.log(`Request finished`);
61 }
62}
63
64import { Injectable } from '@angular/core';
65import { HttpErrorResponse } from '@angular/common/http';
66
67import { MessageService } from 'primeng/api';
68import { TimeoutError } from 'rxjs';
69
70/**
71 * Shows a user-friendly error message when a HTTP request fails.
72 */
73@Injectable({
74 providedIn: 'root'
75})
76export class HttpErrorHandlerService {
77 constructor(private messageService: MessageService) {}
78
79 handle(error: Error | HttpErrorResponse) {
80 if (error instanceof TimeoutError) {
81 return this.openDialog('error', `Няма връзка до сървъра.`);
82 }
83
84 if (error instanceof HttpErrorResponse && error.error && error.error.message) {
85 return this.openDialog('error', error.error.message);
86 }
87
88 if (error instanceof Error) {
89 switch (error.message) {
90 default:
91 return this.openDialog('error', `An unknown error occurred`);
92 }
93 }
94
95 // Generic HTTP errors
96 switch (error.status) {
97 case 400:
98 switch (error.error) {
99 case 'invalid_username_or_password':
100 return this.openDialog('error', 'Невалидно потребителско име или парола');
101 default:
102 return this.openDialog('error', 'Bad request');
103 }
104
105 case 401:
106 return this.openDialog('error', 'Ще трябва да се логнете отново');
107
108 case 403:
109 return this.openDialog('error', `You don't have the required permissions`);
110
111 case 404:
112 return this.openDialog('error', 'Resource not found');
113
114 case 422:
115 return this.openDialog('error', 'Invalid data provided');
116
117 case 500:
118 case 501:
119 case 502:
120 case 503:
121 return this.openDialog('error', 'An internal server error occurred');
122
123 case -1:
124 return this.openDialog(
125 'error',
126 'You appear to be offline. Please check your internet connection and try again.'
127 );
128
129 case 0:
130 return this.openDialog('error', `CORS issue?`);
131
132 default:
133 return this.openDialog('error', `An unknown error occurred`);
134 }
135 }
136
137 private openDialog(severity: string, message: string) {
138 if (message?.trim()) {
139 this.messageService.add({
140 key: 'interceptor',
141 severity: severity,
142 summary: 'Информация',
143 detail: message,
144 life: 3000
145 });
146 }
147 }
148}
149
150
1import { Injectable } from '@angular/core';
2import {
3 HttpEvent,
4 HttpInterceptor,
5 HttpHandler,
6 HttpRequest,
7 HttpErrorResponse,
8 HttpResponse,
9 HttpHeaders
10} from '@angular/common/http';
11import { Observable, EMPTY, finalize, catchError, timeout, map, throwError } from 'rxjs';
12
13import { HttpErrorHandlerService } from '@core/services';
14
15@Injectable()
16export class HttpErrorInterceptor implements HttpInterceptor {
17 private readonly APP_XHR_TIMEOUT = 6000;
18
19 constructor(private errorHandlerService: HttpErrorHandlerService) {}
20
21 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
22 return next.handle(this.performRequest(request)).pipe(
23 timeout(this.APP_XHR_TIMEOUT),
24 map((event: HttpEvent<any>) => this.handleSuccessfulResponse(event)),
25 catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next)),
26 finalize(this.handleRequestCompleted.bind(this))
27 );
28 }
29
30 private performRequest(request: HttpRequest<any>): HttpRequest<any> {
31 let headers: HttpHeaders = request.headers;
32 //headers = headers.set('MyCustomHeaderKey', `MyCustomHeaderValue`);
33 return request.clone({ headers });
34 }
35
36 private handleSuccessfulResponse(event: HttpEvent<any>): HttpEvent<any> {
37 if (event instanceof HttpResponse) {
38 event = event.clone({ body: event.body.response });
39 }
40 return event;
41 }
42
43 private processRequestError(
44 error: HttpErrorResponse,
45 request: HttpRequest<any>,
46 next: HttpHandler
47 ): Observable<HttpEvent<any>> {
48 console.log('http error response');
49
50 if ([401].includes(error.status)) {
51 return throwError(error);
52 }
53
54 this.errorHandlerService.handle(error);
55
56 return throwError(error);
57 }
58
59 private handleRequestCompleted(): void {
60 // console.log(`Request finished`);
61 }
62}
63
64import { Injectable } from '@angular/core';
65import { HttpErrorResponse } from '@angular/common/http';
66
67import { MessageService } from 'primeng/api';
68import { TimeoutError } from 'rxjs';
69
70/**
71 * Shows a user-friendly error message when a HTTP request fails.
72 */
73@Injectable({
74 providedIn: 'root'
75})
76export class HttpErrorHandlerService {
77 constructor(private messageService: MessageService) {}
78
79 handle(error: Error | HttpErrorResponse) {
80 if (error instanceof TimeoutError) {
81 return this.openDialog('error', `Няма връзка до сървъра.`);
82 }
83
84 if (error instanceof HttpErrorResponse && error.error && error.error.message) {
85 return this.openDialog('error', error.error.message);
86 }
87
88 if (error instanceof Error) {
89 switch (error.message) {
90 default:
91 return this.openDialog('error', `An unknown error occurred`);
92 }
93 }
94
95 // Generic HTTP errors
96 switch (error.status) {
97 case 400:
98 switch (error.error) {
99 case 'invalid_username_or_password':
100 return this.openDialog('error', 'Невалидно потребителско име или парола');
101 default:
102 return this.openDialog('error', 'Bad request');
103 }
104
105 case 401:
106 return this.openDialog('error', 'Ще трябва да се логнете отново');
107
108 case 403:
109 return this.openDialog('error', `You don't have the required permissions`);
110
111 case 404:
112 return this.openDialog('error', 'Resource not found');
113
114 case 422:
115 return this.openDialog('error', 'Invalid data provided');
116
117 case 500:
118 case 501:
119 case 502:
120 case 503:
121 return this.openDialog('error', 'An internal server error occurred');
122
123 case -1:
124 return this.openDialog(
125 'error',
126 'You appear to be offline. Please check your internet connection and try again.'
127 );
128
129 case 0:
130 return this.openDialog('error', `CORS issue?`);
131
132 default:
133 return this.openDialog('error', `An unknown error occurred`);
134 }
135 }
136
137 private openDialog(severity: string, message: string) {
138 if (message?.trim()) {
139 this.messageService.add({
140 key: 'interceptor',
141 severity: severity,
142 summary: 'Информация',
143 detail: message,
144 life: 3000
145 });
146 }
147 }
148}
149
150import { Injectable } from '@angular/core';
151import {
152 HttpRequest,
153 HttpHandler,
154 HttpEvent,
155 HttpInterceptor,
156 HttpErrorResponse
157} from '@angular/common/http';
158import {
159 BehaviorSubject,
160 catchError,
161 EMPTY,
162 filter,
163 finalize,
164 Observable,
165 switchMap,
166 take,
167 throwError
168} from 'rxjs';
169
170import { AuthService } from '@core/services';
171import { AuthResponse } from '@core/types';
172
173@Injectable()
174export class AuthInterceptor implements HttpInterceptor {
175 private refreshTokenInProgress: boolean;
176 private refreshToken$ = new BehaviorSubject<AuthResponse | null>(null);
177
178 constructor(private authService: AuthService) {}
179
180 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
181 return next
182 .handle(this.performRequest(request))
183 .pipe(
184 catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next))
185 );
186 }
187
188 private performRequest(request: HttpRequest<any>): HttpRequest<any> {
189 const accessToken = this.authService.getAccessToken();
190
191 let headers = request.headers;
192 if (accessToken) {
193 headers = headers.set('Authorization', `Bearer ${accessToken}`);
194 }
195
196 return request.clone({ headers });
197 }
198
199 private processRequestError(
200 error: HttpErrorResponse,
201 request: HttpRequest<any>,
202 next: HttpHandler
203 ): Observable<HttpEvent<any>> {
204 console.log('auth interceptor called');
205
206 if (
207 error instanceof HttpErrorResponse &&
208 error.status === 401 &&
209 this.authService.isSignedIn()
210 ) {
211 return this.refreshToken(request, next);
212 }
213
214 return throwError(error);
215 }
216
217 private refreshToken(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
218 console.log('refresh token in auth.interceptor called');
219
220 // in case the page consists of more than one requests
221 if (!this.refreshTokenInProgress) {
222 this.refreshToken$.next(null);
223 this.refreshTokenInProgress = true;
224
225 return this.authService.refreshToken().pipe(
226 switchMap((response) => {
227 if (response) {
228 this.refreshToken$.next(response);
229 return next.handle(this.performRequest(request));
230 }
231
232 this.authService.signOut();
233 return throwError(() => new Error("RefreshTokenFailed"));
234 }),
235 catchError((error) => {
236 this.authService.signOut();
237 return throwError(error);
238 }),
239 finalize(() => (this.refreshTokenInProgress = false))
240 );
241 } else {
242 // wait while getting new token
243 return this.refreshToken$.pipe(
244 filter((result) => result !== null),
245 take(1),
246 switchMap(() => next.handle(this.performRequest(request)))
247 );
248 }
249 }
250}
251
252
ANSWER
Answered 2021-Aug-04 at 19:08Instead of this:
1import { Injectable } from '@angular/core';
2import {
3 HttpEvent,
4 HttpInterceptor,
5 HttpHandler,
6 HttpRequest,
7 HttpErrorResponse,
8 HttpResponse,
9 HttpHeaders
10} from '@angular/common/http';
11import { Observable, EMPTY, finalize, catchError, timeout, map, throwError } from 'rxjs';
12
13import { HttpErrorHandlerService } from '@core/services';
14
15@Injectable()
16export class HttpErrorInterceptor implements HttpInterceptor {
17 private readonly APP_XHR_TIMEOUT = 6000;
18
19 constructor(private errorHandlerService: HttpErrorHandlerService) {}
20
21 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
22 return next.handle(this.performRequest(request)).pipe(
23 timeout(this.APP_XHR_TIMEOUT),
24 map((event: HttpEvent<any>) => this.handleSuccessfulResponse(event)),
25 catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next)),
26 finalize(this.handleRequestCompleted.bind(this))
27 );
28 }
29
30 private performRequest(request: HttpRequest<any>): HttpRequest<any> {
31 let headers: HttpHeaders = request.headers;
32 //headers = headers.set('MyCustomHeaderKey', `MyCustomHeaderValue`);
33 return request.clone({ headers });
34 }
35
36 private handleSuccessfulResponse(event: HttpEvent<any>): HttpEvent<any> {
37 if (event instanceof HttpResponse) {
38 event = event.clone({ body: event.body.response });
39 }
40 return event;
41 }
42
43 private processRequestError(
44 error: HttpErrorResponse,
45 request: HttpRequest<any>,
46 next: HttpHandler
47 ): Observable<HttpEvent<any>> {
48 console.log('http error response');
49
50 if ([401].includes(error.status)) {
51 return throwError(error);
52 }
53
54 this.errorHandlerService.handle(error);
55
56 return throwError(error);
57 }
58
59 private handleRequestCompleted(): void {
60 // console.log(`Request finished`);
61 }
62}
63
64import { Injectable } from '@angular/core';
65import { HttpErrorResponse } from '@angular/common/http';
66
67import { MessageService } from 'primeng/api';
68import { TimeoutError } from 'rxjs';
69
70/**
71 * Shows a user-friendly error message when a HTTP request fails.
72 */
73@Injectable({
74 providedIn: 'root'
75})
76export class HttpErrorHandlerService {
77 constructor(private messageService: MessageService) {}
78
79 handle(error: Error | HttpErrorResponse) {
80 if (error instanceof TimeoutError) {
81 return this.openDialog('error', `Няма връзка до сървъра.`);
82 }
83
84 if (error instanceof HttpErrorResponse && error.error && error.error.message) {
85 return this.openDialog('error', error.error.message);
86 }
87
88 if (error instanceof Error) {
89 switch (error.message) {
90 default:
91 return this.openDialog('error', `An unknown error occurred`);
92 }
93 }
94
95 // Generic HTTP errors
96 switch (error.status) {
97 case 400:
98 switch (error.error) {
99 case 'invalid_username_or_password':
100 return this.openDialog('error', 'Невалидно потребителско име или парола');
101 default:
102 return this.openDialog('error', 'Bad request');
103 }
104
105 case 401:
106 return this.openDialog('error', 'Ще трябва да се логнете отново');
107
108 case 403:
109 return this.openDialog('error', `You don't have the required permissions`);
110
111 case 404:
112 return this.openDialog('error', 'Resource not found');
113
114 case 422:
115 return this.openDialog('error', 'Invalid data provided');
116
117 case 500:
118 case 501:
119 case 502:
120 case 503:
121 return this.openDialog('error', 'An internal server error occurred');
122
123 case -1:
124 return this.openDialog(
125 'error',
126 'You appear to be offline. Please check your internet connection and try again.'
127 );
128
129 case 0:
130 return this.openDialog('error', `CORS issue?`);
131
132 default:
133 return this.openDialog('error', `An unknown error occurred`);
134 }
135 }
136
137 private openDialog(severity: string, message: string) {
138 if (message?.trim()) {
139 this.messageService.add({
140 key: 'interceptor',
141 severity: severity,
142 summary: 'Информация',
143 detail: message,
144 life: 3000
145 });
146 }
147 }
148}
149
150import { Injectable } from '@angular/core';
151import {
152 HttpRequest,
153 HttpHandler,
154 HttpEvent,
155 HttpInterceptor,
156 HttpErrorResponse
157} from '@angular/common/http';
158import {
159 BehaviorSubject,
160 catchError,
161 EMPTY,
162 filter,
163 finalize,
164 Observable,
165 switchMap,
166 take,
167 throwError
168} from 'rxjs';
169
170import { AuthService } from '@core/services';
171import { AuthResponse } from '@core/types';
172
173@Injectable()
174export class AuthInterceptor implements HttpInterceptor {
175 private refreshTokenInProgress: boolean;
176 private refreshToken$ = new BehaviorSubject<AuthResponse | null>(null);
177
178 constructor(private authService: AuthService) {}
179
180 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
181 return next
182 .handle(this.performRequest(request))
183 .pipe(
184 catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next))
185 );
186 }
187
188 private performRequest(request: HttpRequest<any>): HttpRequest<any> {
189 const accessToken = this.authService.getAccessToken();
190
191 let headers = request.headers;
192 if (accessToken) {
193 headers = headers.set('Authorization', `Bearer ${accessToken}`);
194 }
195
196 return request.clone({ headers });
197 }
198
199 private processRequestError(
200 error: HttpErrorResponse,
201 request: HttpRequest<any>,
202 next: HttpHandler
203 ): Observable<HttpEvent<any>> {
204 console.log('auth interceptor called');
205
206 if (
207 error instanceof HttpErrorResponse &&
208 error.status === 401 &&
209 this.authService.isSignedIn()
210 ) {
211 return this.refreshToken(request, next);
212 }
213
214 return throwError(error);
215 }
216
217 private refreshToken(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
218 console.log('refresh token in auth.interceptor called');
219
220 // in case the page consists of more than one requests
221 if (!this.refreshTokenInProgress) {
222 this.refreshToken$.next(null);
223 this.refreshTokenInProgress = true;
224
225 return this.authService.refreshToken().pipe(
226 switchMap((response) => {
227 if (response) {
228 this.refreshToken$.next(response);
229 return next.handle(this.performRequest(request));
230 }
231
232 this.authService.signOut();
233 return throwError(() => new Error("RefreshTokenFailed"));
234 }),
235 catchError((error) => {
236 this.authService.signOut();
237 return throwError(error);
238 }),
239 finalize(() => (this.refreshTokenInProgress = false))
240 );
241 } else {
242 // wait while getting new token
243 return this.refreshToken$.pipe(
244 filter((result) => result !== null),
245 take(1),
246 switchMap(() => next.handle(this.performRequest(request)))
247 );
248 }
249 }
250}
251
252 catchError((error) => {
253 this.authService.signOut();
254 return throwError(error);
255 }),
256
You could try this:
1import { Injectable } from '@angular/core';
2import {
3 HttpEvent,
4 HttpInterceptor,
5 HttpHandler,
6 HttpRequest,
7 HttpErrorResponse,
8 HttpResponse,
9 HttpHeaders
10} from '@angular/common/http';
11import { Observable, EMPTY, finalize, catchError, timeout, map, throwError } from 'rxjs';
12
13import { HttpErrorHandlerService } from '@core/services';
14
15@Injectable()
16export class HttpErrorInterceptor implements HttpInterceptor {
17 private readonly APP_XHR_TIMEOUT = 6000;
18
19 constructor(private errorHandlerService: HttpErrorHandlerService) {}
20
21 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
22 return next.handle(this.performRequest(request)).pipe(
23 timeout(this.APP_XHR_TIMEOUT),
24 map((event: HttpEvent<any>) => this.handleSuccessfulResponse(event)),
25 catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next)),
26 finalize(this.handleRequestCompleted.bind(this))
27 );
28 }
29
30 private performRequest(request: HttpRequest<any>): HttpRequest<any> {
31 let headers: HttpHeaders = request.headers;
32 //headers = headers.set('MyCustomHeaderKey', `MyCustomHeaderValue`);
33 return request.clone({ headers });
34 }
35
36 private handleSuccessfulResponse(event: HttpEvent<any>): HttpEvent<any> {
37 if (event instanceof HttpResponse) {
38 event = event.clone({ body: event.body.response });
39 }
40 return event;
41 }
42
43 private processRequestError(
44 error: HttpErrorResponse,
45 request: HttpRequest<any>,
46 next: HttpHandler
47 ): Observable<HttpEvent<any>> {
48 console.log('http error response');
49
50 if ([401].includes(error.status)) {
51 return throwError(error);
52 }
53
54 this.errorHandlerService.handle(error);
55
56 return throwError(error);
57 }
58
59 private handleRequestCompleted(): void {
60 // console.log(`Request finished`);
61 }
62}
63
64import { Injectable } from '@angular/core';
65import { HttpErrorResponse } from '@angular/common/http';
66
67import { MessageService } from 'primeng/api';
68import { TimeoutError } from 'rxjs';
69
70/**
71 * Shows a user-friendly error message when a HTTP request fails.
72 */
73@Injectable({
74 providedIn: 'root'
75})
76export class HttpErrorHandlerService {
77 constructor(private messageService: MessageService) {}
78
79 handle(error: Error | HttpErrorResponse) {
80 if (error instanceof TimeoutError) {
81 return this.openDialog('error', `Няма връзка до сървъра.`);
82 }
83
84 if (error instanceof HttpErrorResponse && error.error && error.error.message) {
85 return this.openDialog('error', error.error.message);
86 }
87
88 if (error instanceof Error) {
89 switch (error.message) {
90 default:
91 return this.openDialog('error', `An unknown error occurred`);
92 }
93 }
94
95 // Generic HTTP errors
96 switch (error.status) {
97 case 400:
98 switch (error.error) {
99 case 'invalid_username_or_password':
100 return this.openDialog('error', 'Невалидно потребителско име или парола');
101 default:
102 return this.openDialog('error', 'Bad request');
103 }
104
105 case 401:
106 return this.openDialog('error', 'Ще трябва да се логнете отново');
107
108 case 403:
109 return this.openDialog('error', `You don't have the required permissions`);
110
111 case 404:
112 return this.openDialog('error', 'Resource not found');
113
114 case 422:
115 return this.openDialog('error', 'Invalid data provided');
116
117 case 500:
118 case 501:
119 case 502:
120 case 503:
121 return this.openDialog('error', 'An internal server error occurred');
122
123 case -1:
124 return this.openDialog(
125 'error',
126 'You appear to be offline. Please check your internet connection and try again.'
127 );
128
129 case 0:
130 return this.openDialog('error', `CORS issue?`);
131
132 default:
133 return this.openDialog('error', `An unknown error occurred`);
134 }
135 }
136
137 private openDialog(severity: string, message: string) {
138 if (message?.trim()) {
139 this.messageService.add({
140 key: 'interceptor',
141 severity: severity,
142 summary: 'Информация',
143 detail: message,
144 life: 3000
145 });
146 }
147 }
148}
149
150import { Injectable } from '@angular/core';
151import {
152 HttpRequest,
153 HttpHandler,
154 HttpEvent,
155 HttpInterceptor,
156 HttpErrorResponse
157} from '@angular/common/http';
158import {
159 BehaviorSubject,
160 catchError,
161 EMPTY,
162 filter,
163 finalize,
164 Observable,
165 switchMap,
166 take,
167 throwError
168} from 'rxjs';
169
170import { AuthService } from '@core/services';
171import { AuthResponse } from '@core/types';
172
173@Injectable()
174export class AuthInterceptor implements HttpInterceptor {
175 private refreshTokenInProgress: boolean;
176 private refreshToken$ = new BehaviorSubject<AuthResponse | null>(null);
177
178 constructor(private authService: AuthService) {}
179
180 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
181 return next
182 .handle(this.performRequest(request))
183 .pipe(
184 catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next))
185 );
186 }
187
188 private performRequest(request: HttpRequest<any>): HttpRequest<any> {
189 const accessToken = this.authService.getAccessToken();
190
191 let headers = request.headers;
192 if (accessToken) {
193 headers = headers.set('Authorization', `Bearer ${accessToken}`);
194 }
195
196 return request.clone({ headers });
197 }
198
199 private processRequestError(
200 error: HttpErrorResponse,
201 request: HttpRequest<any>,
202 next: HttpHandler
203 ): Observable<HttpEvent<any>> {
204 console.log('auth interceptor called');
205
206 if (
207 error instanceof HttpErrorResponse &&
208 error.status === 401 &&
209 this.authService.isSignedIn()
210 ) {
211 return this.refreshToken(request, next);
212 }
213
214 return throwError(error);
215 }
216
217 private refreshToken(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
218 console.log('refresh token in auth.interceptor called');
219
220 // in case the page consists of more than one requests
221 if (!this.refreshTokenInProgress) {
222 this.refreshToken$.next(null);
223 this.refreshTokenInProgress = true;
224
225 return this.authService.refreshToken().pipe(
226 switchMap((response) => {
227 if (response) {
228 this.refreshToken$.next(response);
229 return next.handle(this.performRequest(request));
230 }
231
232 this.authService.signOut();
233 return throwError(() => new Error("RefreshTokenFailed"));
234 }),
235 catchError((error) => {
236 this.authService.signOut();
237 return throwError(error);
238 }),
239 finalize(() => (this.refreshTokenInProgress = false))
240 );
241 } else {
242 // wait while getting new token
243 return this.refreshToken$.pipe(
244 filter((result) => result !== null),
245 take(1),
246 switchMap(() => next.handle(this.performRequest(request)))
247 );
248 }
249 }
250}
251
252 catchError((error) => {
253 this.authService.signOut();
254 return throwError(error);
255 }),
256 catchError((error) => {
257 this.authService.signOut();
258 return throwError(() => error);
259 }),
260
I wasn't able to test it thoroughly, but a simple attempt seemed to work.
This was my simple test (using RxJS v7.2):
Service
1import { Injectable } from '@angular/core';
2import {
3 HttpEvent,
4 HttpInterceptor,
5 HttpHandler,
6 HttpRequest,
7 HttpErrorResponse,
8 HttpResponse,
9 HttpHeaders
10} from '@angular/common/http';
11import { Observable, EMPTY, finalize, catchError, timeout, map, throwError } from 'rxjs';
12
13import { HttpErrorHandlerService } from '@core/services';
14
15@Injectable()
16export class HttpErrorInterceptor implements HttpInterceptor {
17 private readonly APP_XHR_TIMEOUT = 6000;
18
19 constructor(private errorHandlerService: HttpErrorHandlerService) {}
20
21 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
22 return next.handle(this.performRequest(request)).pipe(
23 timeout(this.APP_XHR_TIMEOUT),
24 map((event: HttpEvent<any>) => this.handleSuccessfulResponse(event)),
25 catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next)),
26 finalize(this.handleRequestCompleted.bind(this))
27 );
28 }
29
30 private performRequest(request: HttpRequest<any>): HttpRequest<any> {
31 let headers: HttpHeaders = request.headers;
32 //headers = headers.set('MyCustomHeaderKey', `MyCustomHeaderValue`);
33 return request.clone({ headers });
34 }
35
36 private handleSuccessfulResponse(event: HttpEvent<any>): HttpEvent<any> {
37 if (event instanceof HttpResponse) {
38 event = event.clone({ body: event.body.response });
39 }
40 return event;
41 }
42
43 private processRequestError(
44 error: HttpErrorResponse,
45 request: HttpRequest<any>,
46 next: HttpHandler
47 ): Observable<HttpEvent<any>> {
48 console.log('http error response');
49
50 if ([401].includes(error.status)) {
51 return throwError(error);
52 }
53
54 this.errorHandlerService.handle(error);
55
56 return throwError(error);
57 }
58
59 private handleRequestCompleted(): void {
60 // console.log(`Request finished`);
61 }
62}
63
64import { Injectable } from '@angular/core';
65import { HttpErrorResponse } from '@angular/common/http';
66
67import { MessageService } from 'primeng/api';
68import { TimeoutError } from 'rxjs';
69
70/**
71 * Shows a user-friendly error message when a HTTP request fails.
72 */
73@Injectable({
74 providedIn: 'root'
75})
76export class HttpErrorHandlerService {
77 constructor(private messageService: MessageService) {}
78
79 handle(error: Error | HttpErrorResponse) {
80 if (error instanceof TimeoutError) {
81 return this.openDialog('error', `Няма връзка до сървъра.`);
82 }
83
84 if (error instanceof HttpErrorResponse && error.error && error.error.message) {
85 return this.openDialog('error', error.error.message);
86 }
87
88 if (error instanceof Error) {
89 switch (error.message) {
90 default:
91 return this.openDialog('error', `An unknown error occurred`);
92 }
93 }
94
95 // Generic HTTP errors
96 switch (error.status) {
97 case 400:
98 switch (error.error) {
99 case 'invalid_username_or_password':
100 return this.openDialog('error', 'Невалидно потребителско име или парола');
101 default:
102 return this.openDialog('error', 'Bad request');
103 }
104
105 case 401:
106 return this.openDialog('error', 'Ще трябва да се логнете отново');
107
108 case 403:
109 return this.openDialog('error', `You don't have the required permissions`);
110
111 case 404:
112 return this.openDialog('error', 'Resource not found');
113
114 case 422:
115 return this.openDialog('error', 'Invalid data provided');
116
117 case 500:
118 case 501:
119 case 502:
120 case 503:
121 return this.openDialog('error', 'An internal server error occurred');
122
123 case -1:
124 return this.openDialog(
125 'error',
126 'You appear to be offline. Please check your internet connection and try again.'
127 );
128
129 case 0:
130 return this.openDialog('error', `CORS issue?`);
131
132 default:
133 return this.openDialog('error', `An unknown error occurred`);
134 }
135 }
136
137 private openDialog(severity: string, message: string) {
138 if (message?.trim()) {
139 this.messageService.add({
140 key: 'interceptor',
141 severity: severity,
142 summary: 'Информация',
143 detail: message,
144 life: 3000
145 });
146 }
147 }
148}
149
150import { Injectable } from '@angular/core';
151import {
152 HttpRequest,
153 HttpHandler,
154 HttpEvent,
155 HttpInterceptor,
156 HttpErrorResponse
157} from '@angular/common/http';
158import {
159 BehaviorSubject,
160 catchError,
161 EMPTY,
162 filter,
163 finalize,
164 Observable,
165 switchMap,
166 take,
167 throwError
168} from 'rxjs';
169
170import { AuthService } from '@core/services';
171import { AuthResponse } from '@core/types';
172
173@Injectable()
174export class AuthInterceptor implements HttpInterceptor {
175 private refreshTokenInProgress: boolean;
176 private refreshToken$ = new BehaviorSubject<AuthResponse | null>(null);
177
178 constructor(private authService: AuthService) {}
179
180 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
181 return next
182 .handle(this.performRequest(request))
183 .pipe(
184 catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next))
185 );
186 }
187
188 private performRequest(request: HttpRequest<any>): HttpRequest<any> {
189 const accessToken = this.authService.getAccessToken();
190
191 let headers = request.headers;
192 if (accessToken) {
193 headers = headers.set('Authorization', `Bearer ${accessToken}`);
194 }
195
196 return request.clone({ headers });
197 }
198
199 private processRequestError(
200 error: HttpErrorResponse,
201 request: HttpRequest<any>,
202 next: HttpHandler
203 ): Observable<HttpEvent<any>> {
204 console.log('auth interceptor called');
205
206 if (
207 error instanceof HttpErrorResponse &&
208 error.status === 401 &&
209 this.authService.isSignedIn()
210 ) {
211 return this.refreshToken(request, next);
212 }
213
214 return throwError(error);
215 }
216
217 private refreshToken(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
218 console.log('refresh token in auth.interceptor called');
219
220 // in case the page consists of more than one requests
221 if (!this.refreshTokenInProgress) {
222 this.refreshToken$.next(null);
223 this.refreshTokenInProgress = true;
224
225 return this.authService.refreshToken().pipe(
226 switchMap((response) => {
227 if (response) {
228 this.refreshToken$.next(response);
229 return next.handle(this.performRequest(request));
230 }
231
232 this.authService.signOut();
233 return throwError(() => new Error("RefreshTokenFailed"));
234 }),
235 catchError((error) => {
236 this.authService.signOut();
237 return throwError(error);
238 }),
239 finalize(() => (this.refreshTokenInProgress = false))
240 );
241 } else {
242 // wait while getting new token
243 return this.refreshToken$.pipe(
244 filter((result) => result !== null),
245 take(1),
246 switchMap(() => next.handle(this.performRequest(request)))
247 );
248 }
249 }
250}
251
252 catchError((error) => {
253 this.authService.signOut();
254 return throwError(error);
255 }),
256 catchError((error) => {
257 this.authService.signOut();
258 return throwError(() => error);
259 }),
260 getProducts(): Observable<IProduct[]> {
261 return this.http.get<IProduct[]>(this.productUrl)
262 .pipe(
263 tap(data => console.log('All: ', JSON.stringify(data))),
264 catchError(this.handleError)
265 );
266 }
267
268 private handleError(err: HttpErrorResponse): Observable<never> {
269 // just a test ... more could would go here
270 return throwError(() => err);
271 }
272
Notice that err
here is of type HttpErrorResponse
.
Component
1import { Injectable } from '@angular/core';
2import {
3 HttpEvent,
4 HttpInterceptor,
5 HttpHandler,
6 HttpRequest,
7 HttpErrorResponse,
8 HttpResponse,
9 HttpHeaders
10} from '@angular/common/http';
11import { Observable, EMPTY, finalize, catchError, timeout, map, throwError } from 'rxjs';
12
13import { HttpErrorHandlerService } from '@core/services';
14
15@Injectable()
16export class HttpErrorInterceptor implements HttpInterceptor {
17 private readonly APP_XHR_TIMEOUT = 6000;
18
19 constructor(private errorHandlerService: HttpErrorHandlerService) {}
20
21 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
22 return next.handle(this.performRequest(request)).pipe(
23 timeout(this.APP_XHR_TIMEOUT),
24 map((event: HttpEvent<any>) => this.handleSuccessfulResponse(event)),
25 catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next)),
26 finalize(this.handleRequestCompleted.bind(this))
27 );
28 }
29
30 private performRequest(request: HttpRequest<any>): HttpRequest<any> {
31 let headers: HttpHeaders = request.headers;
32 //headers = headers.set('MyCustomHeaderKey', `MyCustomHeaderValue`);
33 return request.clone({ headers });
34 }
35
36 private handleSuccessfulResponse(event: HttpEvent<any>): HttpEvent<any> {
37 if (event instanceof HttpResponse) {
38 event = event.clone({ body: event.body.response });
39 }
40 return event;
41 }
42
43 private processRequestError(
44 error: HttpErrorResponse,
45 request: HttpRequest<any>,
46 next: HttpHandler
47 ): Observable<HttpEvent<any>> {
48 console.log('http error response');
49
50 if ([401].includes(error.status)) {
51 return throwError(error);
52 }
53
54 this.errorHandlerService.handle(error);
55
56 return throwError(error);
57 }
58
59 private handleRequestCompleted(): void {
60 // console.log(`Request finished`);
61 }
62}
63
64import { Injectable } from '@angular/core';
65import { HttpErrorResponse } from '@angular/common/http';
66
67import { MessageService } from 'primeng/api';
68import { TimeoutError } from 'rxjs';
69
70/**
71 * Shows a user-friendly error message when a HTTP request fails.
72 */
73@Injectable({
74 providedIn: 'root'
75})
76export class HttpErrorHandlerService {
77 constructor(private messageService: MessageService) {}
78
79 handle(error: Error | HttpErrorResponse) {
80 if (error instanceof TimeoutError) {
81 return this.openDialog('error', `Няма връзка до сървъра.`);
82 }
83
84 if (error instanceof HttpErrorResponse && error.error && error.error.message) {
85 return this.openDialog('error', error.error.message);
86 }
87
88 if (error instanceof Error) {
89 switch (error.message) {
90 default:
91 return this.openDialog('error', `An unknown error occurred`);
92 }
93 }
94
95 // Generic HTTP errors
96 switch (error.status) {
97 case 400:
98 switch (error.error) {
99 case 'invalid_username_or_password':
100 return this.openDialog('error', 'Невалидно потребителско име или парола');
101 default:
102 return this.openDialog('error', 'Bad request');
103 }
104
105 case 401:
106 return this.openDialog('error', 'Ще трябва да се логнете отново');
107
108 case 403:
109 return this.openDialog('error', `You don't have the required permissions`);
110
111 case 404:
112 return this.openDialog('error', 'Resource not found');
113
114 case 422:
115 return this.openDialog('error', 'Invalid data provided');
116
117 case 500:
118 case 501:
119 case 502:
120 case 503:
121 return this.openDialog('error', 'An internal server error occurred');
122
123 case -1:
124 return this.openDialog(
125 'error',
126 'You appear to be offline. Please check your internet connection and try again.'
127 );
128
129 case 0:
130 return this.openDialog('error', `CORS issue?`);
131
132 default:
133 return this.openDialog('error', `An unknown error occurred`);
134 }
135 }
136
137 private openDialog(severity: string, message: string) {
138 if (message?.trim()) {
139 this.messageService.add({
140 key: 'interceptor',
141 severity: severity,
142 summary: 'Информация',
143 detail: message,
144 life: 3000
145 });
146 }
147 }
148}
149
150import { Injectable } from '@angular/core';
151import {
152 HttpRequest,
153 HttpHandler,
154 HttpEvent,
155 HttpInterceptor,
156 HttpErrorResponse
157} from '@angular/common/http';
158import {
159 BehaviorSubject,
160 catchError,
161 EMPTY,
162 filter,
163 finalize,
164 Observable,
165 switchMap,
166 take,
167 throwError
168} from 'rxjs';
169
170import { AuthService } from '@core/services';
171import { AuthResponse } from '@core/types';
172
173@Injectable()
174export class AuthInterceptor implements HttpInterceptor {
175 private refreshTokenInProgress: boolean;
176 private refreshToken$ = new BehaviorSubject<AuthResponse | null>(null);
177
178 constructor(private authService: AuthService) {}
179
180 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
181 return next
182 .handle(this.performRequest(request))
183 .pipe(
184 catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next))
185 );
186 }
187
188 private performRequest(request: HttpRequest<any>): HttpRequest<any> {
189 const accessToken = this.authService.getAccessToken();
190
191 let headers = request.headers;
192 if (accessToken) {
193 headers = headers.set('Authorization', `Bearer ${accessToken}`);
194 }
195
196 return request.clone({ headers });
197 }
198
199 private processRequestError(
200 error: HttpErrorResponse,
201 request: HttpRequest<any>,
202 next: HttpHandler
203 ): Observable<HttpEvent<any>> {
204 console.log('auth interceptor called');
205
206 if (
207 error instanceof HttpErrorResponse &&
208 error.status === 401 &&
209 this.authService.isSignedIn()
210 ) {
211 return this.refreshToken(request, next);
212 }
213
214 return throwError(error);
215 }
216
217 private refreshToken(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
218 console.log('refresh token in auth.interceptor called');
219
220 // in case the page consists of more than one requests
221 if (!this.refreshTokenInProgress) {
222 this.refreshToken$.next(null);
223 this.refreshTokenInProgress = true;
224
225 return this.authService.refreshToken().pipe(
226 switchMap((response) => {
227 if (response) {
228 this.refreshToken$.next(response);
229 return next.handle(this.performRequest(request));
230 }
231
232 this.authService.signOut();
233 return throwError(() => new Error("RefreshTokenFailed"));
234 }),
235 catchError((error) => {
236 this.authService.signOut();
237 return throwError(error);
238 }),
239 finalize(() => (this.refreshTokenInProgress = false))
240 );
241 } else {
242 // wait while getting new token
243 return this.refreshToken$.pipe(
244 filter((result) => result !== null),
245 take(1),
246 switchMap(() => next.handle(this.performRequest(request)))
247 );
248 }
249 }
250}
251
252 catchError((error) => {
253 this.authService.signOut();
254 return throwError(error);
255 }),
256 catchError((error) => {
257 this.authService.signOut();
258 return throwError(() => error);
259 }),
260 getProducts(): Observable<IProduct[]> {
261 return this.http.get<IProduct[]>(this.productUrl)
262 .pipe(
263 tap(data => console.log('All: ', JSON.stringify(data))),
264 catchError(this.handleError)
265 );
266 }
267
268 private handleError(err: HttpErrorResponse): Observable<never> {
269 // just a test ... more could would go here
270 return throwError(() => err);
271 }
272 ngOnInit(): void {
273 this.sub = this.productService.getProducts().subscribe({
274 next: products => {
275 this.products = products;
276 this.filteredProducts = this.products;
277 },
278 error: err => this.errorMessage = err.message
279 });
280 }
281
Here I was able to retrieve the message
property from the error response and display it in my UI.
Let me know if this works for you.
QUESTION
Google Colab drive mount (with underscore) is not working
Asked 2022-Feb-13 at 11:04Until yesterday (20 Jan) I could connect to another google drive account (using drive._mount), but when I tried this today, google colab showed me this error:
1from google.colab import drive
2drive._mount('/content/drive/')
3
4 /usr/local/lib/python3.7/dist-packages/google/colab/drive.py in _mount(mountpoint, force_remount, timeout_ms, use_metadata_server, ephemeral)
5 294 wrote_to_fifo = True
6 295 elif case == 5 and not use_metadata_server:
7--> 296 raise ValueError('mount failed: invalid oauth code')
8 297 elif case == 6:
9 298 # Terminate the DriveFS binary before killing bash.
10
11ValueError: mount failed: invalid oauth code
12
Strange thing is that error tells me "invalid oauth code", but not let me connect to google page and copy the code!
And I set use_metadata_server=True but this time, new error appeares:
1from google.colab import drive
2drive._mount('/content/drive/')
3
4 /usr/local/lib/python3.7/dist-packages/google/colab/drive.py in _mount(mountpoint, force_remount, timeout_ms, use_metadata_server, ephemeral)
5 294 wrote_to_fifo = True
6 295 elif case == 5 and not use_metadata_server:
7--> 296 raise ValueError('mount failed: invalid oauth code')
8 297 elif case == 6:
9 298 # Terminate the DriveFS binary before killing bash.
10
11ValueError: mount failed: invalid oauth code
12 from google.colab import drive
13 drive._mount('/content/drive/', use_metadata_server=True)
14ValueError Traceback (most recent call last)
15<ipython-input-5-42a561ce7057> in <module>()
16 1 from google.colab import drive
17----> 2 drive._mount('/content/drive/', use_metadata_server=True)
18
19/usr/local/lib/python3.7/dist-packages/google/colab/drive.py in _mount(mountpoint, force_remount, timeout_ms, use_metadata_server, ephemeral)
20 285 ': timeout during initial read of root folder; for more info: '
21 286 'https://research.google.com/colaboratory/faq.html#drive-timeout')
22--> 287 raise ValueError('mount failed' + extra_reason)
23 288 elif case == 2:
24 289 # Not already authorized, so do the authorization dance.
25
26ValueError: mount failed
27
Also I used drive.mount but showed pop-up and ask me to enter another account credentials. When I enter it, this error appears:
1from google.colab import drive
2drive._mount('/content/drive/')
3
4 /usr/local/lib/python3.7/dist-packages/google/colab/drive.py in _mount(mountpoint, force_remount, timeout_ms, use_metadata_server, ephemeral)
5 294 wrote_to_fifo = True
6 295 elif case == 5 and not use_metadata_server:
7--> 296 raise ValueError('mount failed: invalid oauth code')
8 297 elif case == 6:
9 298 # Terminate the DriveFS binary before killing bash.
10
11ValueError: mount failed: invalid oauth code
12 from google.colab import drive
13 drive._mount('/content/drive/', use_metadata_server=True)
14ValueError Traceback (most recent call last)
15<ipython-input-5-42a561ce7057> in <module>()
16 1 from google.colab import drive
17----> 2 drive._mount('/content/drive/', use_metadata_server=True)
18
19/usr/local/lib/python3.7/dist-packages/google/colab/drive.py in _mount(mountpoint, force_remount, timeout_ms, use_metadata_server, ephemeral)
20 285 ': timeout during initial read of root folder; for more info: '
21 286 'https://research.google.com/colaboratory/faq.html#drive-timeout')
22--> 287 raise ValueError('mount failed' + extra_reason)
23 288 elif case == 2:
24 289 # Not already authorized, so do the authorization dance.
25
26ValueError: mount failed
27from google.colab import drive
28drive.mount('/content/drive/')
29
30MessageError Traceback (most recent call last)
31<ipython-input-1-91874b305a32> in <module>()
32 1 from google.colab import drive
33----> 2 drive.mount('/content/drive/')
34
353 frames
36/usr/local/lib/python3.7/dist-packages/google/colab/_message.py in read_reply_from_input(message_id, timeout_sec)
37 104 reply.get('colab_msg_id') == message_id):
38 105 if 'error' in reply:
39--> 106 raise MessageError(reply['error'])
40 107 return reply.get('data', None)
41 108
42
43MessageError: Error: credential propagation was unsuccessful
44
I think this is new policy. Is there an solution?
ANSWER
Answered 2022-Jan-21 at 14:00Alright, until this problem get solved, I did this trick for my project:
I shared which files I need (like datasets) with my other accounts. For this, you should:
- Go to your google drive (where your file is stored) then right-click on it and choose "Share"
- Click on "Change to anyone with the link"
- Copy link and open it in new window
- In top-right side, click on your google accounts list and select which one you need
- At the opened window, in top-right side click on "Add shortcut to Drive" and choose location where you want to save file in it
- Your file now is accessible in account you did choose
QUESTION
Save authenticated users to database coming from Azure AD
Asked 2022-Feb-10 at 15:47I am working on a simple web app for learning purposes using Angular for the frontend and Java Spring for the backend. I don't have a particular problem that I want you guys to help me out with, instead I have a question about OAuth2 authentication.
I have registered my Angular SPA in Azure AD (Authorization Code Flow + PKCE), I set up roles and everything is working okay. My question is what do I do when authenticated users ping my backend? My backend has no information about the users.
I thought of a solution to make a web filter, and every time an authenticated user pings any endpoint requiring the user to be authenticated, to check the database if the user exists (through the username), and save him if he does not exist. I'm pretty sure this will work, but I don't think this is the best solution, considering my web filter will have to read from the databases for every single HTTP request that comes in, and write to the database occasionally (if the user logs in for the first time).
I shouldn't be worried about performance issues because I'm building this strictly for learning purposes, but nevertheless I want to do this the right way. I tried googling this in multiple ways, but I guess I'm not using the right keywords to find what I'm looking for. Any opinion or advice would be much appreciated! Thanks!
EDIT: I followed this article to achieve the OAuth2 + OIDC authentication and authorization, my security config in the backend is the same: https://ordina-jworks.github.io/security/2020/08/18/Securing-Applications-Azure-AD.html
ANSWER
Answered 2022-Feb-10 at 15:47Post the discussion with clarity on the requirements. If you want to use have the following:
- Accept an Azure AD logged in user to consumer your web service
- You would want to check if the user exists in your application database with minimal network latency.
With the requirement of not always hitting your Database, one option is to use a cache.
The ideal solution for this cache to work is:
- Ensure the cache is checked for every HTTP Request using Web Filter
- Make sure the cache is always updated with the latest users being logged in via Azure AD
Example:
Implement a CacheService.java
1package com.example.springboot;
2
3import java.util.Collections;
4
5import org.apache.catalina.User;
6import org.springframework.cache.CacheManager;
7import org.springframework.cache.annotation.Cacheable;
8import org.springframework.cache.concurrent.ConcurrentMapCache;
9import org.springframework.cache.support.SimpleCacheManager;
10import org.springframework.context.annotation.Bean;
11import org.springframework.stereotype.Component;
12
13@Component
14public class CacheService {
15
16 @Bean
17 public CacheManager cacheManager() {
18 SimpleCacheManager cacheManager = new SimpleCacheManager();
19 cacheManager.setCaches(Collections.singletonList(new ConcurrentMapCache("users")));
20 return cacheManager;
21 }
22
23
24 @Cacheable(cacheNames = "users")
25 public User getUser(String username) {
26 // Code below will not execute after the first calling for the given username.
27 // So if one username is already cached, it would not invoke for the same user again from the DB.
28
29 // Get or Create a new user based on the Database call
30 return null;
31 }
32}
33
Then implement a web filter like:
1package com.example.springboot;
2
3import java.util.Collections;
4
5import org.apache.catalina.User;
6import org.springframework.cache.CacheManager;
7import org.springframework.cache.annotation.Cacheable;
8import org.springframework.cache.concurrent.ConcurrentMapCache;
9import org.springframework.cache.support.SimpleCacheManager;
10import org.springframework.context.annotation.Bean;
11import org.springframework.stereotype.Component;
12
13@Component
14public class CacheService {
15
16 @Bean
17 public CacheManager cacheManager() {
18 SimpleCacheManager cacheManager = new SimpleCacheManager();
19 cacheManager.setCaches(Collections.singletonList(new ConcurrentMapCache("users")));
20 return cacheManager;
21 }
22
23
24 @Cacheable(cacheNames = "users")
25 public User getUser(String username) {
26 // Code below will not execute after the first calling for the given username.
27 // So if one username is already cached, it would not invoke for the same user again from the DB.
28
29 // Get or Create a new user based on the Database call
30 return null;
31 }
32}
33package com.example.springboot;
34
35import java.io.IOException;
36
37import javax.servlet.FilterChain;
38import javax.servlet.ServletException;
39import javax.servlet.ServletRequest;
40import javax.servlet.ServletResponse;
41
42import org.springframework.beans.factory.annotation.Autowired;
43import org.springframework.stereotype.Component;
44import org.springframework.web.filter.GenericFilterBean;
45
46@Component
47public class CredentialsInjectionFilter extends GenericFilterBean {
48
49 @Autowired
50 private CacheService cacheService;
51
52 @Override
53 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
54 FilterChain filterChain) throws IOException, ServletException {
55
56 cacheService.getUser("my_username");
57
58 filterChain.doFilter(servletRequest, servletResponse);
59 }
60}
61
More on Caching with Springboot: https://www.javadevjournal.com/spring/spring-caching/
Community Discussions contain sources that include Stack Exchange Network
Tutorials and Learning Resources in Authorization
Tutorials and Learning Resources are not available at this moment for Authorization