angular2 basic authorization on get request - 405 - drupal

I'm trying to get basic authorization to work for a Get request, but I'm getting 2 exceptions:
OPTIONS http://localhost/drupal/user/1?_format=json
XMLHttpRequest cannot load http://localhost/drupal/user/1?_format=json. Response for preflight has invalid HTTP status code 405
I'm using angular2 with drupal 8 backend
here is my service
var _baseUrl = "http://localhost/drupal";
#Injectable()
export class DrupalService {
private actionUrl: string;
constructor(private _http: Http, private _apiUrl: DrupalApi) {
this.actionUrl = _baseUrl + _apiUrl;
}
authHeaders() {
let username = 'username';
let password = 'password';
let token = btoa(username + ':' + password);
var headers = new Headers();
headers.append('Authorization', 'Basic ' + token);
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
return headers;
}
public GetSingle = (id: number): Observable<Response> => {
return this._http.get(this.actionUrl + id + '?_format=json'
, {headers: this.authHeaders()}).map(res => res.json());
}
}
but it works when I try the same request from postman app
how can I fix it?

In Postman, you are sending a header:
Authorization: Basic bXVyaGFmOmhleGFkZWNpbWFsMDU
But in angular you are passing:
headers.append('Authorization', 'Basic ' + btoa('bXVyaGFmOmhleGFkZWNpbWFsMDU'));
which will end up being:
Authorization: Basic YlhWeWFHRm1PbWhsZUdGa1pXTnBiV0ZzTURV
So, just don't convert the string to base64
headers.append('Authorization', 'Basic bXVyaGFmOmhleGFkZWNpbWFsMDU');
Update
Error code 405 means : Method Not Allowed, Which means, drupal does not allow OPTIONS requests. I've not worked with Drupal before. But, there should be a way to allow OPTIONS requests.

Related

Ionic 3 - Http Get 401 (Unauthorized)

I’m calling a service using a token
Failed to load resource: the server responded with a status of 401 (Unauthorized)
Http failure response for http://localhost:65291/api/post: 401 Unauthorized
The same call works in Postman with Headers;
Content-Type: application/json
Authorization: Bearer token
The function in ionic is
getPosts() {
var header = new HttpHeaders({ "Content-Type": "application/json" });
header.append("Authorization", "Bearer " + this.token);
console.log("Bearer " + this.token);
return new Promise(resolve => {
console.log(this.apiUrl + '/post');
this.http.get(this.apiUrl + '/post', { headers: header}).subscribe((data: Post[]) => {
resolve(data);
}, err => {
console.log(err);
});
});
}
Added a log for the token to be sure that is adding it to the header correctly (the token is fine).
The apiUrl variable has value http://localhost:65291/api.
What is wrong here? Cors is enabled… Postman works ok…
Thanks
I think you definitely have client side problem (since its 401 and also you mention Postman works ok).
I had similar issues when I tried to append headers in the same fashion you did so I would suggest trying this (to eliminate this problem):
getPosts() {
// try forming headers object in one go:
let token = "Bearer "+this.token
let headers = new HttpHeaders({
"Content-Type": "application/json",
"Authorization": token
});
// now here I am not sure why you do promise wrapping this way, but then I would suggest:
return this.http.get(this.apiUrl + '/post', { headers: headers })
.toPromise()
.then((data: Post[]) => { // Success
console.log(data);
resolve(data);
}, (err) => {
console.log(err);
});
}
If the problem is still there - please share which version of Angular and Http module you are using?
Also check out this issue here: How to correctly set Http Request Header in Angular 2
And specifically this answer if you are on Angular 4.3+:
How to correctly set Http Request Header in Angular 2
After a while I found the problem,
header.append("Authorization", "Bearer " + this.token); is wrong. It worked using
let headers = new HttpHeaders({"Authorization: " + "Bearer " + this.token})
Setting multiple headers:
this.http
.post('api/items/add', body, {
headers: new HttpHeaders({
'Authorization': 'my-auth-token',
'x-header': 'x-value'
})
}).subscribe()
I had a similar problem, it works on postman and cors enabled but in the app doesn't work, my problem was i have / at the end of the URL in the API security config, and i was making the request without /, i just remove it from request URL,
also you can add /* in security config or put / in your app, the URL must be the same.
(maybe you have solved your issue and it was different issue but this is a possibe solution)

Angular 5 - HTTP post

I have a backend interface which I invoke with my Angular 1.3 application without problems. With my Angular 5 application I get an HTTP 403 (Forbidden)
I faced the request parameters in an picture (Angular 1.3 at the left side, Angular 5 at the right side):
My Angular 5 code looks like this:
createDate(calendarEvent: CalendarEvent) {
let serialDates = false;
let calendarEventSerialDateType = 'NO_SERIAL_DATE';
let serialEndDate = this.utilService.convertDateToDateString(new Date());
let url: string = environment.apiEndpoint + 'calendarevents/calendarevent/' + serialDates + '/' + calendarEventSerialDateType + '/' + serialEndDate + '/';
let headers = new Headers({ 'Content-Type': 'application/json', 'X-AUTH-TOKEN': this.authService.getToken()});
let options = new RequestOptions({ headers: headers });
return this.http.post(url, calendarEvent, options).map(res => res.json()).subscribe(res => console.log(res));
}
I have e.g. no idea why X-AUTH-TOKEN is not set with Angular 5 because I set it in the headers object with
let headers = new Headers({ 'Content-Type': 'application/json', 'X-AUTH-TOKEN': this.authService.getToken()});
and why OPTIONS is mentioned at Request Method with Angular 5 instead of POST like with angular 1.3.
Does anyone have any idea what I am doing wrong?
let Options = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
return this.http.post(url, calendarEvent, Options).map(res => res.json()).subscribe(res => console.log(res));
OPTIONS request is considered as a pre-flight request, which is sent before the actual request to check the existence of method.
If the request sent is a valid one, it will call the valid method.
And regarding the request header, you can use the one in the above answer.
let Options = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
For Angular5,
import { HttpClient, HttpHeaders } from '#angular/common/http';
const headers = new HttpHeaders().set('X-AUTH-TOKEN', this.authService.getToken());
By default, 'Content-Type': 'application/json'
Stop using map.
Subscribe the response and store it to Observable for further access.
Example:
createDate(calendarEvent: CalendarEvent) {
let serialDates = false;
let calendarEventSerialDateType = 'NO_SERIAL_DATE';
let serialEndDate = this.utilService.convertDateToDateString(new Date());
let url: string = environment.apiEndpoint + 'calendarevents/calendarevent/' + serialDates + '/' + calendarEventSerialDateType + '/' + serialEndDate + '/';
let headers = new HttpHeaders().set('X-AUTH-TOKEN', this.authService.getToken());
return this.http.post(url, calendarEvent, {headers: headers}).subscribe(res => console.log(res));
}

How to get custom response header in angular 2?

I am new to angular 2 and currently working with angular 2.2.1 in which I am successfully able to post request and get success response however when I try to get Authorization header from response I always get null whether I am able to get Content-Type header. Below is my solution so far.
service.ts login method:
login(model: LoginModel) {
let requestUrl = '/someurl';
let requestPayload = JSON.stringify(model);
let headers = this.getHeaders(false); // ... Set all required headers
let options = new RequestOptions({ headers: headers }); // Create a request option
return this.http.post(requestUrl, requestPayload, options) // ...using post request
//.map((res: Response)) // ...and calling .json() on the response to return data
.subscribe((res: Response) => {
var payload = res.json();
var authorization = res.headers.get('Authorization');
var contentType = res.headers.get('Content-Type');
console.log(payload, contentType, authorization)
});
}
Header Helper
getHeaders(isSecureAPI: boolean) {
let headers = new Headers({ 'Content-Type': 'application/json', 'Accept': 'application/json' });
if (isSecureAPI) {
headers.append('Authorization', 'GetFromSession');
headers.append('X-UserID', 'GetFromSession');
}
return headers;
}
Fiddler track:
Angular Console Output
So anyone can guide me what I am possibly doing wrong?
Header was allowed but not exposed on CORS server however adding headers.add("Access-Control-Expose-Headers", "Authorization, X-Custom"); on server did the job :)
I've been trying to find a solution and came across this
Let's say I'm using the Microsoft Cors WebAPI 2 Nuget package, and I have the following global configuration in my WebApiConfig.cs:
...
var corsAttr = new EnableCorsAttribute("http://localhost:4200", "*", "*");
config.EnableCors(corsAttr);
...
The EnableCorsAttribute constructor accepts a 4th string parameter to globally allow any additional headers:
var corsAttr = new EnableCorsAttribute("http://localhost:4200", "*", "*", "X-Foo, X-Bar, X-Baz");

.NET Core WebAPI + OpenIdDict (credentials flow) and Angular2 client: 401 after successful login (full repro)

I'm trying to create an Angular2 SPA consuming a .NET Core Web API protected using OpenIdDict, with credentials flow. In creating a repro solution for this issue, I have also detailed all my steps in a readme, so hope this post can be useful to newbies like me. Please find the full repro solutions in these repositories:
server-side (.NET Core + OpenIdDict), with detailed instructions to build your own: https://github.com/Myrmex/repro-oidang
client-side (Angular2): https://github.com/Myrmex/repro-angoid
As for the server side, I followed the sample provided by OpenIdDict about this flow (https://github.com/openiddict/openiddict-samples/blob/master/samples/PasswordFlow). Here are the most relevant bits in Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddEntityFrameworkSqlServer()
.AddDbContext<CatalogContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Catalog")))
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Catalog")));
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddOpenIddict<ApplicationDbContext>()
.DisableHttpsRequirement()
.EnableTokenEndpoint("/connect/token")
.EnableLogoutEndpoint("/connect/logout")
.EnableUserinfoEndpoint("/connect/userinfo")
.AllowPasswordFlow()
.AllowRefreshTokenFlow()
.AddEphemeralSigningKey();
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
});
// add my services
// ...
services.AddTransient<IDatabaseInitializer, DatabaseInitializer>();
services.AddSwaggerGen();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory,
IDatabaseInitializer databaseInitializer)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
loggerFactory.AddNLog();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseCors(builder =>
builder.WithOrigins("http://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod());
app.UseOAuthValidation();
app.UseOpenIddict();
app.UseMvc();
databaseInitializer.Seed().GetAwaiter().GetResult();
env.ConfigureNLog("nlog.config");
app.UseSwagger();
app.UseSwaggerUi();
}
If I test it with Fiddler, it works fine: the token request gets the token, and I can then include it in the Authorization header to access any protected API, which returns JSON data as expected.
Sample token request:
POST http://localhost:51346/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=password&scope=offline_access profile email roles&resource=http://localhost:4200&username=...&password=...
Sample resource request:
GET http://localhost:51346/api/values
Content-Type: application/json
Authorization: Bearer ...received token here...
Yet, on the client side, whenever I try the same request I get a 401 error; looking at the log, it seems that Angular2 Http service is not sending the required header at all, as I get the error Authentication was skipped because no bearer token was received (see more log entries below).
A service retrieving some resources is like this:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { SettingsService } from './settings.service';
import { AuthenticationService } from './authentication.service';
export interface ICategory {
id: string;
name: string;
}
#Injectable()
export class CategoryService {
constructor(
private _http: Http,
private _settings: SettingsService,
private _authService: AuthenticationService) { }
public getCategories(): Observable<ICategory[]> {
let url = this._settings.apiBaseUrl + 'categories';
let options = {
headers: this._authService.createAuthHeaders({
'Content-Type': 'application/json'
})
};
return this._http.get(url, options).map((res: Response) => res.json())
.catch((error: any) => Observable.throw(error.json().error || 'server error'));
}
}
Where the helper createAuthHeaders just gets some properties representing the Header (https://angular.io/docs/ts/latest/api/http/index/Headers-class.html) entries, retrieves the stored token, appends the Authentication entry to the header, and returns it:
public createAuthHeaders(headers?: { [name: string]: any }): Headers {
let auth = new Headers();
if (headers) {
for (let key in headers) {
if (headers.hasOwnProperty(key)) {
auth.append(key, headers[key]);
}
}
}
let tokenResult = this._localStorage.retrieve(this._settings.tokenStorageKey, true);
if (tokenResult) {
auth.append('Authentication', 'Bearer ' + tokenResult.access_token);
}
return auth;
}
Yet, this request gets a 401 response and then Angular throws when trying to mapping the response to a JSON object (Unexpected end of JSON input).
I must add that as soon as the client gets the token, it makes another request with it, to retrieve user info, and this works fine; here is it (see the code after get user info):
public login(name: string, password: string) {
let body = 'grant_type=password&scope=offline_access profile email roles' +
`&resource=${this._settings.appBaseUrl}&username=${name}&password=${password}`;
this._http.post(
this._settings.authBaseUrl + `token`,
body,
{
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
})
}).map(res => res.json())
.subscribe(
(token: ITokenResult) => {
if (token.expires_in) {
token.expires_on = this.calculateExpirationDate(+token.expires_in);
}
this._localStorage.store(this._settings.tokenStorageKey, token, true);
// get user info
this._http.get(this._settings.authBaseUrl + 'userinfo', {
headers: new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token.access_token
})
}).map(res => res.json())
.subscribe((info: IUserInfoResult) => {
let user: IUser = {
id: info.name,
email: info.email,
name: info.name,
firstName: info.given_name,
lastName: info.family_name,
role: info.role,
verified: info.email_verified
};
this._localStorage.store(this._settings.userStorageKey, user, true);
this.userChanged.emit(user);
}, error => {
console.log(error);
});
},
error => {
console.log(error);
});
}
Yet, any other request, built using the service above, fails. What's wrong with the headers built with the quoted function?
Here are some log entries on the server side:
2016-11-18 20:41:31.9815|0|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|DEBUG| Authentication was skipped because no bearer token was received.
2016-11-18 20:41:31.9815|0|OpenIddict.Infrastructure.OpenIddictProvider|INFO| The token request validation process was skipped because the client_id parameter was missing or empty.
2016-11-18 20:41:32.0715|0|AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerMiddleware|INFO| No explicit audience was associated with the access token.
2016-11-18 20:41:32.1165|10|AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerMiddleware|INFO| AuthenticationScheme: ASOS signed in.
2016-11-18 20:41:32.1635|3|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|INFO| HttpContext.User merged via AutomaticAuthentication from authenticationScheme: Bearer.
2016-11-18 20:41:57.7430|0|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|DEBUG| Authentication was skipped because no bearer token was received.
2016-11-18 20:41:57.7430|0|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|DEBUG| Authentication was skipped because no bearer token was received.
2016-11-18 20:41:57.8820|12|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|INFO| AuthenticationScheme: Bearer was challenged.
2016-11-18 20:41:57.9305|12|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|INFO| AuthenticationScheme: Bearer was challenged.
2016-11-18 20:41:57.9465|0|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|DEBUG| Authentication was skipped because no bearer token was received.
2016-11-18 20:41:57.9925|12|AspNet.Security.OAuth.Validation.OAuthValidationMiddleware|INFO| AuthenticationScheme: Bearer was challenged.
Your bearer token usage is incorrect.
auth.append('Authentication', 'Bearer ' + tokenResult.access_token) // wrong
auth.append('Authorization', 'Bearer ' + tokenResult.access_token) // right
The header needs to be Authorization. See https://www.rfc-editor.org/rfc/rfc6750#section-2.1

How to read received headers in Angular 2?

Can some body tell me how to read received headers in Angular 2?
i have mad a request, for login and password, and there should be sent back headers with Token. I need the token for further workaround.
here is part of the code:
sendLogin(username, password) {
let body = JSON.stringify({"username": username, "password": password});
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http.post(this.loginUrl, body, options)
.map(res => res.json())
.map((res) => {
if (res.ok) {
// at least how to console.log received headers?
console.log( res.headers); //undefined
this.loggedIn = res.ok;
} return res.ok;
});
};
thank you.
Most of the time such an issue is related to CORS. You need to explicitly enable allowed headers in the response headers.
You're only be able to see the header in the map only if it's enabled by CORS.
Your server needs to return the following in headers:
Access-Control-Allow-Headers: X-SomeHeader

Resources