I'm using the Ktor Client dsl to add the auth plugin like this:
import io.ktor.client.*
import io.ktor.client.engine.okhttp.*
import io.ktor.client.plugins.auth.*
import io.ktor.client.plugins.auth.providers.*
import io.ktor.client.plugins.logging.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.coroutines.runBlocking``
val client = HttpClient(OkHttp) {
install(Auth) {
basic {
credentials {
BasicAuthCredentials(email, token)
}
}
}
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}
}
But the logger does not show any Basic authentication header:
15:47:14.589 [DefaultDispatcher-worker-3] INFO io.ktor.client.HttpClient - REQUEST: https://example.url.net/api
METHOD: HttpMethod(value=GET)
COMMON HEADERS
-> Accept: */*
-> Accept-Charset: UTF-8
// >>> SHOULD BE HERE <<<
CONTENT HEADERS
-> Content-Length: 0
BODY Content-Type: null
BODY START
When adding the headers for a GET through this other method now works:
fun search() {
runBlocking {
val response: HttpResponse = client.get(url) {
headers {
append(HttpHeaders.Authorization, "Basic $auth")
}
}
println(response.bodyAsText())
}
}
The request has all the correct headers:
15:49:47.407 [DefaultDispatcher-worker-3] INFO io.ktor.client.HttpClient - REQUEST: https://example.url.net/api
METHOD: HttpMethod(value=GET)
COMMON HEADERS
-> Accept: */*
-> Accept-Charset: UTF-8
-> Authorization: Basic fwouwneg34g38gngwg // HERE
CONTENT HEADERS
-> Content-Length: 0
BODY Content-Type: null
BODY START
Note: I'm using this dependencies in Kotlin Multiplatform for desktop jvm:
implementation("io.ktor:ktor-client-core:2.1.0")
implementation("io.ktor:ktor-client-okhttp:2.1.0")
implementation("io.ktor:ktor-client-auth:2.1.0")
implementation("io.ktor:ktor-client-logging:2.1.0")
implementation("ch.qos.logback:logback-classic:1.3.0-beta0")
By default, the Authorization header isn't sent for each request but only when a server responds with the 401 status code and the WWW-Authenticate header. To send the Authorization header for each request you can call the sendWithoutRequest method with the { true } lambda.
install(Auth) {
basic {
sendWithoutRequest { true }
}
}
For more information about Basic authentication in Ktor please read the documentation.
Related
Using HapiJS 19 on Linux.
I am in the process of upgrading an older version of a back-end (hapi-16) and in the older version sending form-data would work just fine, but for some reason I get:
{
"statusCode": 415,
"error": "Unsupported Media Type",
"message": "Unsupported Media Type"
}
It seems as if I need something added to my hapi server in order to accept form-data.
Here is my server file:
const hapi = require('#hapi/hapi');
// const HapiAuthCookie = require('hapi-auth-cookie');
const CORS = { cors: true };
const SERVER_SETTINGS = { host: 'localhost', port: 8040, routes: CORS };
const server = hapi.server(SERVER_SETTINGS);
const endpoints = require('./routes.js');
// connect to database
require('../db/dbconn.js');
module.exports = {
start: async() => {
// plugin registration
await server.register([]);
server.route(endpoints,
{ prefix: '/dart-api' }
);
// start server
try {
await server.start();
console.log(`Server running on: ${server.info.uri}`);
} catch (error) {
console.log(error);
}
}
};
// Graceful server stop
process.on('SIGINT', () => {
/* eslint-disable-next-line */
console.warn('\n\n>> Stopping Hapi server ...');
server.stop({ timeout: 100 }).then((err) => {
if (err) {
console.log('Hapi server stopped');
throw err;
}
});
}); // server.stop {ends}
process.on('unhandledRejection', (err) => {
console.log(err);
throw err;
});
I'll appreciate some help.
Thanks in advance.
Update: I've done some testing with different clients with the same result. Here is the response when I use curl:
curl http://localhost:8040/users/avatars -X PUT -F 'files=#/home/ralvez/.downloads/ICONS/ricavatar.png' -vvv
* Trying 127.0.0.1:8040...
* Connected to localhost (127.0.0.1) port 8040 (#0)
> PUT /users/avatars HTTP/1.1
> Host: localhost:8040
> User-Agent: curl/7.70.0
> Accept: */*
> Content-Length: 17433
> Content-Type: multipart/form-data; boundary=------------------------0e59b1780748d1d6
>
* We are completely uploaded and fine
* Mark bundle as not supporting multiuse
< HTTP/1.1 415 Unsupported Media Type
< content-type: application/json; charset=utf-8
< vary: origin
< access-control-expose-headers: WWW-Authenticate,Server-Authorization
< cache-control: no-cache
< content-length: 86
< Date: Fri, 22 May 2020 17:27:01 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
{"statusCode":415,"error":"Unsupported Media Type","message":"Unsupported Media Type"}
This make no sense to me since my handler is like this:
{
method: 'PUT',
path: '/users/avatars',
handler: users.updateAvatar,
options: {
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
}
}
},
there is no doubt in my mind that this is ready to accept form-data, yet it is rejecting with "statusCode": 415, "error": "Unsupported Media Type",
what gives?!
OK. After much searching and after realizing this HAD to be related to the version of HapiJS I'm using I found someone else had the same problem.
Here is the link:
[Fix Hapi version 19.0.3 error 415 unsupported media type upload file with multipart/form-data
So I decided to change my route as follows:
{
method: 'PUT',
path: '/users/avatars',
handler: users.updateAvatar,
options: {
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data',
multipart: true
}
}
},
and it works.
A shorter version also works:
{
method: 'PUT',
path: '/users/avatars',
handler: users.updateAvatar,
options: {
payload: {
multipart: true
}
}
},
I have create API on laravel 5.8 using passport Auth.
I have the following API routes on laravel :
// private routes
Route::middleware('auth:api')->group(function () {
Route::get('/getUser', 'Api\AuthController#getUser')->name('getUser');
});
// public routes
Route::post('/login', 'Api\AuthController#login')->name('login.api');
Route::post('/register', 'Api\AuthController#register')->name('register.api');
I test my routes using postman and that works!
but when I have call the routing from angular 8, the public routes works, but the private route return the error 401 (Unauthorized).
The error from console
GET http://localhost:8000/api/getUser 401 (Unauthorized) zone-evergreen.js:2952
ERROR HttpErrorResponse {headers: HttpHeaders, status: 401, statusText: "Unauthorized", url: "http://localhost:8000/api/getUser",
ok: false, …} core.js:6014
the following code for call the routes in angular:
import { Headers, RequestOptions } from '#angular/http';
import { HttpClient } from '#angular/common/http';
headers: Headers = new Headers();
constructor(private http: HttpClient) {}
getUser(accessToken) {
this.headers.append('Accept','application/json');
this.headers.append('Authorization', 'Bearer ' + accessToken);
this.options = new RequestOptions({headers: this.headers});
return this.http.get(this.urlServer, this.options);
}
I have tried a lot of way to solve this problem and see all tickets here,
but the issue not solved yet.
Finally I found the way to solve this problem...
I change my way of request by import the old Http from angular/http
instead of HttpClient from angular/common/http..
so the new code of angular is :
import { Headers, RequestOptions, Http } from '#angular/http';
constructor(private http: Http) {}
getUser(accessToken) {
return this.http.get(this.server + 'getUser', this.options).pipe( map(res => res.json()));
}
I am trying to do put request to the server ,but I am getting 401 error
The provider
putData(){
header = header.append('Content-Type', 'application/json');
header = header.append('Accept', 'application/json');
header = header.append('Authorization', 'Bearer');
return this.http.put('http://something//', {headers: header})
.subscribe((result: any) => {
console.log(result);
}, (errorResponse) => {
console.error(errorResponse);
}
home.ts
this.MyProvider.putData();
I have attach the network header as well since the first request success, but the second does not ?!!!
import { HttpClient, HttpParams, HttpHeaders} from '#angular/common/http';
constructor(public http: HttpClient )
putData(){
const headers = new HttpHeaders({'Authorization':'Basic'});
return this.http.post("'http://something//'", {headers: headers});
}
i think you code header is not adding properly try this method to add header
I am working in an Angular 6 application and I was wondering what should be the best practice when customizing the url while sending requests to the server.
Here is the scenario:
- In my Angular project I have the environment.ts and environment.prod.ts where I added a "host" which contains the url:port of the http server (project with the controllers).
- I am creating Services to be injected in my components which will be responsible for sending requests (GETs and POSTs) to the server to retrieve data or to send updates.
- I want to use the "host" from the environment.ts as part of the request url. So ALL my requests will have the "host" as the base url and then i can concatenate to the desired path.
I already checked a few solutions and I already implemented one of them, but I am not sure this is the right practice. I will write below what i implemented so far and then i will write some ideas, please help me understand what is the best solution (I am new at angular)
Currently implemented:
-> In my feature services, like LoginService, I inject the angular HttpClient. Then I simply call:
return this.httpService.post("/login/", creds).pipe(
map((data: any) => {
this.manager = data;
return this.manager;
}));
I created an interceptor to make changes to the url: InterceptService implements HttpInterceptor where I create a new instance of the HttpRequest and customize the request.url using environment.host. I also needed the interceptor to add a Header for the authentication (still not fully implemented)
const httpRequest = new HttpRequest(<any>request.method, environment.host + request.url, request.body);
request = Object.assign(request, httpRequest);
const headers = new HttpHeaders({
'Authorization': 'Bearer token 123',
'Content-Type': 'application/json'
});
Questions:
1) This works, all my requests are changed in the interceptor as I
wanted, but it doesn't look like the best practice in my first look. I
don't like to create a new HeepRequest to be able to do this (i did it
to keep it immutable, I guess that's the correct way). Do you think
this looks good?
2) What about the Authentication being added to the Header in the interceptor? Is it ok? Most of the references I checked did this
Other solutions:
1) I saw some examples where a HttpClientService extends Http and each of the methods such as get and post edit the url and headers before calling super methods. But I believe this is not Angular 6 and is probably not preferrable
2) I could also create a service that receives an angular HttpClient (angular 6 HttpClientModule) instance by injection and I could implement the methods like get or post.
Well, as I didn't get any answers I will add my solution. i believe it's the best solution based on my researches.
I used an interceptor for adding information to the header such as the
token bearer authentication.
import { Injectable } from '#angular/core';
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
HttpResponse,
HttpHeaders,
HttpErrorResponse
} from '#angular/common/http'
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { environment } from "../../../environments/environment";
import { Router } from "#angular/router";
export class HttpClientInterceptor implements HttpInterceptor {
constructor(private router: Router) { }
// intercept request to add information to the headers such as the token
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
//I decided to remove this logic from the interceptor to add the host url on the HttpClientService I created
//const httpRequest = new HttpRequest(<any>request.method, environment.host + request.url, request.body);
//request = Object.assign(request, httpRequest);
var token = localStorage.getItem("bearerToken");
if (token) {
const newReq = request.clone(
{
headers: request.headers.set('Authorization',
'Bearer ' + token)
});
return next.handle(newReq).pipe(
tap(event => {
if (event instanceof HttpResponse) {
console.log("Interceptor - HttpResponse = " + event.status); // http response status code
}
}, error => {
// http response status code
if (error instanceof HttpErrorResponse) {
console.log("----response----");
console.error("status code:");
console.error(error.status);
console.error(error.message);
console.log("--- end of response---");
if (error.status === 401 || error.status === 403) //check if the token expired and redirect to login
this.router.navigate(['login']);
}
})
)
}
else {
return next.handle(request);
}
};
For changing the url, I created a service on file
http-client.service.ts and got the host url from environment.ts
import { Injectable } from "#angular/core";
import { HttpClient } from '#angular/common/http';
import { Observable } from "rxjs";
import { environment } from "../../../environments/environment";
#Injectable({ providedIn:'root' })
export class HttpClientService {
constructor(private http: HttpClient) { }
get(url: string, options?: any): Observable<ArrayBuffer> {
url = this.updateUrl(url);
return this.http.get(url, options);
}
post(url: string, body: string, options?: any): Observable<ArrayBuffer> {
url = this.updateUrl(url);
return this.http.post(url, body, options);
}
put(url: string, body: string, options?: any): Observable<ArrayBuffer> {
url = this.updateUrl(url);
return this.http.put(url, body, options);
}
delete(url: string, options?: any): Observable<ArrayBuffer> {
url = this.updateUrl(url);
return this.http.delete(url,options);
}
private updateUrl(req: string) {
return environment.host + req;
}
}
As i said, I believe this is the best approach, but feel free to add information to my question/answer.
I'm trying to request a HTTP GET call to my local REST REST http://demosite.com/mage_auth/api/rest it needs an authorization token to let a user call an endpoint.
So in my request I passed headers.set('Authorization', token) and content-type JSON, however it doesn't seems to be passing the header in the Request's Header when I check the network response.
I've created a httpClient Service to pass the auth token: --
createAuthorizationHeader(headers: Headers) {
var sample3Results = (new OAuthSimple()).sign({
path: 'http://www.demosites.com/mage_auth/',
signatures: {
'consumer_key': key,
'shared_secret': secret,
'access_token': token,
'access_secret': tokensecret
}
});
try {
console.debug('Sample 3', sample3Results);
} catch (e) { };
let headerAuth = sample3Results.header;
headers.set('Authorization', headerAuth);
headers.set('Content-Type', 'application/json; charset=UTF-8' );
}
get(url) {
let headers = new Headers();
this.createAuthorizationHeader(headers);
return this.http.get(url, {
headers: headers
});
}
My component request look like this: --
this.httClient.get('http://www.demosites.com/mage_auth/api/rest/products')
.map(res => res.json())
.subscribe(data => {
console.log('data: ', data);
})
The REST API is running on WAMP server, so I've also added some CORS values in httpd.conf
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "origin, content-type"
Header set Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
</IfModule>
And I'm still getting this Error.
XMLHttpRequest cannot load http://www.demosites.com/mage_auth/api/rest/products. Response for preflight has invalid HTTP status code 400
Just in case, I also added a proxy.config.json from Angular-cli as thought that'd fix it because the request is coming from localhost:4200. But seems wasn't the case, I'm out of idea why it still giving a preflight error.
Can someone point out what's wrong with this request?
That can be a misconfiguration of the CORS filter server side.
As for me, even if my CORS filter was well configured server side, I still faced the same issue. I used the RequestOptions of Angular for the headers. This is how I soved it within my angular service
Angular 2
header.append("Content-Type", "application/json");
header.append("X-Auth-Token", token);
let options = new RequestOptions({headers: header})
return this.http.get(url, options)
.toPromise()
.then(response => response.json())
.catch(this.handleError);
Angular 4.3
Define an interceptor
import {Injectable} from '#angular/core';
import {HttpRequest, HttpHandler, HttpEvent, HttpInterceptor} from '#angular/common/http';
import {Observable} from 'rxjs/Observable';
import {AppService} from '../app.service';
#Injectable()
export class Interceptor implements HttpInterceptor {
constructor() {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = request.clone({
setHeaders: {
token: `Bearer token`
}
});
return next.handle(request);
}
}
HttpRequests are immutable objects. So we need to copy them and then modidfy the copy.
Import the interceptor in app.module.ts
...
imports: [
BrowserModule,
HttpClientModule,
...
],
providers: [
{provide: HTTP_INTERCEPTORS, useClass: Interceptor, multi: true},
AppService
...
]
Use the interceptor in app.service.ts
import { Injectable } from '#angular/core';
import {Http, RequestOptions} from '#angular/http';
import 'rxjs/add/operator/map';
import {Observable} from 'rxjs/Observable';
import {HttpClient} from '#angular/common/http';
#Injectable()
export class AppService {
constructor(public http: HttpClient) { }
connectServer() {
return this.http.get('url')
.map(response => response);
}
}