代码流身份验证 Angular 8 无法获取访问令牌
Code Flow Authentication Angular 8 unable to get access token
在我的代码中,我实现了 SSO 登录。我有 auth.service
,它在 app.module.ts 中的 app initializer
中给出。我有代码流,我可以打服务,拿到url里面的代码。现在我想发送一个 post 请求,其中携带一个 body
,它将 return 访问令牌。但是一旦 post 请求被命中,应用程序就会刷新并且进程再次启动。它在多次尝试后退出循环并说 Authorization code has expired
。我的代码如下:
在 app.module.ts 文件中
export function appsso(http: HttpClient, authService: AuthService) {
return (): Promise<any> => {
return authService.init()
};
{provide: APP_INITIALIZER, useFactory: appsso, deps: [HttpClient, AuthService], multi: true}
在 authService 文件中
constructor(private http: HttpClient) {}
init() {
return new Promise<void>((resolve, reject) => {
console.log('init is called');
if (localStorage.getItem('token')) { // if we have token, its good to go
console.log('good to go');
} else {
if (!location.href.includes('?code=')){ // if url contains code
const params = [
'response_type=code',
'client_id=abcde',
'scope=openid profile',
'redirect_uri=https://URLhere/a.0/',
];
location.href = 'https://secureURLhere/authorize?' + params.join('&');
return
} else {
const code = window.location.href.split('=')[1]; //split url to extract code
return this.getAuthToken(code).then(data): any => {
localStorage.setItem('tokenData', data);
console.log('access token received');
resolve();}, error ((err) => {
console.log ('error occured');
reject();
}
}
}
getAuthToken(code: string){
let body: HttpParams = new HttpParams();
body = body.append('grant_type', 'authorization_code')
body = body.append('code', code)
body = body.append('client_id', 'abcde')
body = body.append('client_secret', '12345');
return this.http.post('https://secureURLhere/token', body).toPromise(); //making post call
}
此外 header
的类型为 'Content-Type': 'application/x-www-form-urlencoded'
当 Post API 被命中时,我应该能够获得访问令牌。但它会再次刷新应用程序。如何解决?
编辑 1:
您是否忘记了“return”来自 getAuthToken() 方法内部的承诺?
对我来说,您似乎正在调用 getAuthToken 但得到的是空值。
编辑 2:
我没有看到任何 http.post 会导致应用程序刷新的内容。
唯一会导致重新加载的是这一行
location.href = 'https://secureURLhere/authorize?' + params.join('&');
我觉得这里的 if 条件有问题:
location.href.includes('?code=')
您可能希望在设置参数时在 getAuthToken 中检查该部分。我认为您得到的是 &code= 而不是预期的 ?code=
我可能是错的。
编辑 3
查看代码,我猜这就是 POST 请求的运行方式:
POST https://secureURLhere/token HTTP1.1
(Below is body part)
grant_type=authorization_code&code=code&client_id=abcde&client_secret=12345
您可以在 chrome 开发工具的网络选项卡中查看它以进行验证。将其展示给您的 API 人员或检查 API 的文档以了解预期内容。至于用户定义的 headers,它应该是 HttpHeaders 类型,并且应该是 object 中 http.post() 的第三个参数,属性 称为“headers ”。默认情况下,http 客户端会为你设置一些 headers。
关于它的工作原理,http.post return是一个您可以订阅的可观察对象。在您的情况下,您正在将 observable 转换为承诺并进一步处理它。
什么样的可观察对象?如果你问,那么这取决于 Api 发送的实际响应。它可以是字符串的可观察对象(Observable)、Blob 的可观察对象或具有完整 http 响应的可观察对象(Observable)等。http.post 有 15 种不同的重载。您指定要如何阅读响应。您可以在创建请求时在 http.post 中设置 responseType 或使用泛型和类型转换。
Check the documentation for clarity.
有很多东西在您的代码中不起作用。对于 if 语句的每一端,你应该 return 一些东西。否则可能会卡住。此外,有时您需要在 if-statement 中 return 以防止嵌套,通过这样做您的代码将更易于阅读(大多数情况下)
您的服务代码如下所示:
import { HttpClient, HttpParams } from '@angular/common/http';
export interface AuthTokenResponse {
access_token: string;
id_token: string;
expire_in: number;
token_type: string;
}
class AuthService {
constructor(private http: HttpClient) {}
init() {
return new Promise<void>((resolve, reject) => {
// Check if token is in local storage
if (localStorage.getItem('auth-token')) {
// We have a token, so can continue
return resolve();
}
// Create object from the URL query parameters
const params = window.location.search
.substr(1)
.split('&')
.reduce((prev, current) => {
const [key, value] = current.split('=');
if (key || (value !== undefined && value !== null)) {
prev[key] = value;
}
return prev;
}, {});
// Check if the code is in the parameters
if (params && params['code']) {
// Code present. Use code to get Auth token
return this.getAuthToken(params['code']).then(
(data) => {
// We get a access token. Save the token to the local storage
localStorage.setItem('auth-token', JSON.stringify(data));
resolve();
},
(err) => {
// Something went wrong
reject();
}
);
}
// No token was present in the parameters, so redirect to SSO URL
const redirectParams = new HttpParams()
.set('response_type', 'code')
.set('client_id', 'abcde')
.set('scope', 'openid profile')
.set('redirect_uri', 'https://URLhere/a.0/');
location.href =
'https://secureURLhere/authorize?' + redirectParams.toString();
return reject();
});
}
getAuthToken(code: string): Promise<AuthTokenResponse> {
// Create the body used tor the URL to get the auth token
const body = new HttpParams()
.append('grant_type', 'authorization_code')
.append('code', code)
.append('client_id', 'abcde')
.append('client_secret', '12345');
// Send API Post request to SSO API
return this.http
.post<AuthTokenResponse>('https://secureURLhere/token', body)
.toPromise(); //making post call
}
}
在我的代码中,我实现了 SSO 登录。我有 auth.service
,它在 app.module.ts 中的 app initializer
中给出。我有代码流,我可以打服务,拿到url里面的代码。现在我想发送一个 post 请求,其中携带一个 body
,它将 return 访问令牌。但是一旦 post 请求被命中,应用程序就会刷新并且进程再次启动。它在多次尝试后退出循环并说 Authorization code has expired
。我的代码如下:
在 app.module.ts 文件中
export function appsso(http: HttpClient, authService: AuthService) {
return (): Promise<any> => {
return authService.init()
};
{provide: APP_INITIALIZER, useFactory: appsso, deps: [HttpClient, AuthService], multi: true}
在 authService 文件中
constructor(private http: HttpClient) {}
init() {
return new Promise<void>((resolve, reject) => {
console.log('init is called');
if (localStorage.getItem('token')) { // if we have token, its good to go
console.log('good to go');
} else {
if (!location.href.includes('?code=')){ // if url contains code
const params = [
'response_type=code',
'client_id=abcde',
'scope=openid profile',
'redirect_uri=https://URLhere/a.0/',
];
location.href = 'https://secureURLhere/authorize?' + params.join('&');
return
} else {
const code = window.location.href.split('=')[1]; //split url to extract code
return this.getAuthToken(code).then(data): any => {
localStorage.setItem('tokenData', data);
console.log('access token received');
resolve();}, error ((err) => {
console.log ('error occured');
reject();
}
}
}
getAuthToken(code: string){
let body: HttpParams = new HttpParams();
body = body.append('grant_type', 'authorization_code')
body = body.append('code', code)
body = body.append('client_id', 'abcde')
body = body.append('client_secret', '12345');
return this.http.post('https://secureURLhere/token', body).toPromise(); //making post call
}
此外 header
的类型为 'Content-Type': 'application/x-www-form-urlencoded'
当 Post API 被命中时,我应该能够获得访问令牌。但它会再次刷新应用程序。如何解决?
编辑 1: 您是否忘记了“return”来自 getAuthToken() 方法内部的承诺? 对我来说,您似乎正在调用 getAuthToken 但得到的是空值。
编辑 2: 我没有看到任何 http.post 会导致应用程序刷新的内容。 唯一会导致重新加载的是这一行
location.href = 'https://secureURLhere/authorize?' + params.join('&');
我觉得这里的 if 条件有问题:
location.href.includes('?code=')
您可能希望在设置参数时在 getAuthToken 中检查该部分。我认为您得到的是 &code= 而不是预期的 ?code= 我可能是错的。
编辑 3
查看代码,我猜这就是 POST 请求的运行方式:
POST https://secureURLhere/token HTTP1.1
(Below is body part)
grant_type=authorization_code&code=code&client_id=abcde&client_secret=12345
您可以在 chrome 开发工具的网络选项卡中查看它以进行验证。将其展示给您的 API 人员或检查 API 的文档以了解预期内容。至于用户定义的 headers,它应该是 HttpHeaders 类型,并且应该是 object 中 http.post() 的第三个参数,属性 称为“headers ”。默认情况下,http 客户端会为你设置一些 headers。
关于它的工作原理,http.post return是一个您可以订阅的可观察对象。在您的情况下,您正在将 observable 转换为承诺并进一步处理它。
什么样的可观察对象?如果你问,那么这取决于 Api 发送的实际响应。它可以是字符串的可观察对象(Observable)、Blob 的可观察对象或具有完整 http 响应的可观察对象(Observable
有很多东西在您的代码中不起作用。对于 if 语句的每一端,你应该 return 一些东西。否则可能会卡住。此外,有时您需要在 if-statement 中 return 以防止嵌套,通过这样做您的代码将更易于阅读(大多数情况下)
您的服务代码如下所示:
import { HttpClient, HttpParams } from '@angular/common/http';
export interface AuthTokenResponse {
access_token: string;
id_token: string;
expire_in: number;
token_type: string;
}
class AuthService {
constructor(private http: HttpClient) {}
init() {
return new Promise<void>((resolve, reject) => {
// Check if token is in local storage
if (localStorage.getItem('auth-token')) {
// We have a token, so can continue
return resolve();
}
// Create object from the URL query parameters
const params = window.location.search
.substr(1)
.split('&')
.reduce((prev, current) => {
const [key, value] = current.split('=');
if (key || (value !== undefined && value !== null)) {
prev[key] = value;
}
return prev;
}, {});
// Check if the code is in the parameters
if (params && params['code']) {
// Code present. Use code to get Auth token
return this.getAuthToken(params['code']).then(
(data) => {
// We get a access token. Save the token to the local storage
localStorage.setItem('auth-token', JSON.stringify(data));
resolve();
},
(err) => {
// Something went wrong
reject();
}
);
}
// No token was present in the parameters, so redirect to SSO URL
const redirectParams = new HttpParams()
.set('response_type', 'code')
.set('client_id', 'abcde')
.set('scope', 'openid profile')
.set('redirect_uri', 'https://URLhere/a.0/');
location.href =
'https://secureURLhere/authorize?' + redirectParams.toString();
return reject();
});
}
getAuthToken(code: string): Promise<AuthTokenResponse> {
// Create the body used tor the URL to get the auth token
const body = new HttpParams()
.append('grant_type', 'authorization_code')
.append('code', code)
.append('client_id', 'abcde')
.append('client_secret', '12345');
// Send API Post request to SSO API
return this.http
.post<AuthTokenResponse>('https://secureURLhere/token', body)
.toPromise(); //making post call
}
}