Breeze / breeze.bridge.angular

A bridge that configures Breeze and Angular to work together.
19 stars 5 forks source link

Authorization header #2

Closed ganySA closed 8 years ago

ganySA commented 8 years ago

Hi

Struggling to pass a Authorization header with my breeze requests. Is there an example where i can configure breeze to use a header when making requests...

What does breeze use in the background to make requests? How can i configure the HTTP request process to inject authorization header....

marcelgood commented 8 years ago

Breeze relies on third-party services to make the actual HTTP request and stays out of the business of headers etc. Out of the box it comes preconfigured with jQuery.ajax, but just like anything else in Breeze, the http provider is pluggable. The Angular2 bridge replaces jQuery.ajax with Angular 2's own Http service. So, in order to get your header added, you have to directly interact with the Angular 2 Http service. Unfortunately, Angular 2's Http service doesn't currently have interceptors or any easy way to add headers. Angular 1 was much better in that regard and jQuery.ajax also has a straightforward way to add your own headers.

Getting your Auth header in there with Angular 2 is a bit more involved and requires subclassing a couple of Angular 2 services. Here's a plunker that demonstrates three approaches for how to add your own headers/custom logic to the request. Once you've got your header in there it will automatically be there as part of every Http request that Breeze makes.

http://plnkr.co/edit/gMherG3HwUkXsTkIPNrm?p=info

ganySA commented 8 years ago

Hi

Thanks for the feedback and the plnkr. I have looked at the options and indeed no simple way to do this. I tried method number 1 but this seems to not work very well... and i read somewhere on stack that the header cannot be changed using this method... not sure if that is correct.

For #2 i found angular2-jwt library which actually extends http but this means changing the angular 2 bridge to use the extended library and not just http... If my understanding is correct do i replace http bridge to just use.

I have not tried option 3 yet but it also seems quite complex.

Options 2 seems the easiest way - i am correct to assume that the http passed into angular 2 bridge would need to change to angular2-jwt library extended version?

marcelgood commented 8 years ago

angular2-jwt wraps http, which is different from extending it and therefore yes, the bridge would have to change and use AuthHttp instead of Http as the dependency. The approaches in the plunker truly extend the Angular 2 services such as Http and then replace the default service in the dependency injection, so that when the bridge asks for the Http dependency it actually gets the extended one instead of the default Http service.

ganySA commented 8 years ago

Hi

I must admit i am still not having much fun. The challenge i see with both implementations of the plunkr is that both 1 and 2 run only once (during bootstrap and modify headers at this point) - an auth token is not known as this point in time...

My scenario (the common scenario) a login screen the entry point of the application, now when the bootstrap runs for the login the token is not yet known so using the approach for 1 and 2 is not possible... Once a person logs this code does not re-run to populate the token auth header....

Hope this is making sense.... any ideas?

Thanks

marcelgood commented 8 years ago

Yes, that makes sense. For your scenario, approach #3, replacing the XHRBackend with your own should fit. It sets the header for each request. The following article came across my Inbox the other day. I haven't read it yet in detail, but it might help.

http://www.codemag.com/Article/1607031

ganySA commented 8 years ago

Hi

I must admit i am completely stumped at the moment.

I have implemented the following: auth and this works great to intercept the "request" i have added my headers but i think something does not work as required in the angular adapter.

In angular 2 adapter the following is called which uses the request object from the above code.

  if (requestInfo.request) {
            return this.http.request(requestInfo.request)
                .map(extractData)
                .toPromise()
                .then(requestInfo.success)
                .catch(requestInfo.error);
        }

The below method is called:

request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
        return this.intercept(super.request(url, this.getRequestOptionArgs(options)));
    }

getRequestOptions creates the headers, but unfortunately this does not work as in the breeze code this is ignored and the headers are already set previously..

The only header that is pulling through is. headers.set('Content-Type', config.contentType || 'application/json; charset=utf-8');

It is almost like the HTTP instance is generated too early - i am not sure.

Something seems broken - the only way to create a header that the breeze adapter can use is if it is created right up front before the adapter is started as a global default header which does not help for logins...

ganySA commented 8 years ago

why does the angular 2 bridge use http_1 and http ?

ganySA commented 8 years ago

Still trying to get my head around it but changing the request method in the above code to the below seemed to work:

request(url: Request, options?: RequestOptionsArgs): Observable<Response> {
        url.headers.append("Authorization","TEST123");
        return super.request(url, this.getRequestOptionArgs(options));
    }

Going to do some more testing.

marcelgood commented 8 years ago

We wrote the core of the bridge a while ago, so I had to take another look at what it is actually doing when putting together the request and it turns out, you can set the headers w/o going through as many hoops. It's not very clean at the moment. I have to look into properly exposing access to the headers in a future version, but for now, you can do the following to set arbitrary headers. Keep in mind, that this will only affect requests issued by breeze. If you use the Http service directly in parts of your application, you'll have to set the headers there as well.

import { config } from 'breeze-client';

let ajaxAdapter = <any>config.getAdapterInstance('ajax');
let headers = ajaxAdapter.defaultSettings['headers'] || {};
headers['foo'] = 'bar';
ajaxAdapter.defaultSettings['headers'] = headers;
marcelgood commented 8 years ago

BTW, I believe the issue you are running into with your interceptor is that http.request(..) ignores the options parameter if the url parameter is a Request object. The Request object already contains the headers.

You have to change the interceptor and if the url parameter is a Request object, then add your headers to Request.headers, and otherwise add it to the options.

export declare class Request {
    /**
     * Http method with which to perform the request.
     */
    method: RequestMethod;
    /**
     * {@link Headers} instance
     */
    headers: Headers;
    /** Url of the remote resource */
    url: string;
    /** Body of the request **/
    private _body;
    /** Type of the request body **/
    private contentType;
    /** Enable use credentials */
    withCredentials: boolean;
    constructor(requestOptions: RequestArgs);
    /**
     * Returns the request's body as string, assuming that body exists. If body is undefined, return
     * empty
     * string.
     */
    text(): string;
    /**
     * Returns the request's body as JSON string, assuming that body exists. If body is undefined,
     * return
     * empty
     * string.
     */
    json(): string;
    /**
     * Returns the request's body as array buffer, assuming that body exists. If body is undefined,
     * return
     * null.
     */
    arrayBuffer(): ArrayBuffer;
    /**
     * Returns the request's body as blob, assuming that body exists. If body is undefined, return
     * null.
     */
    blob(): Blob;
    /**
     * Returns the content type of request's body based on its type.
     */
    detectContentType(): ContentType;
    /**
     * Returns the request's body according to its type. If body is undefined, return
     * null.
     */
    getBody(): any;
}
ganySA commented 8 years ago

Hi Marcel

Thanks you are indeed correct this was exactly what the problem was. I am sure this will be a common question. Maybe we can leave it open for a bit.

Thank you for all your help - much appreciated.

rodneyjoyce commented 7 years ago

@ganySA FYI, there is a conversation on this thread re. Breeze and Auth0 with ng2 which you may find useful: https://github.com/Breeze/breeze.js/issues/173#issuecomment-263229251

rodneyjoyce commented 7 years ago

FYI, Breeze/ng2 now works with Auth0 fine - see https://github.com/Breeze/breeze.js/issues/173#issuecomment-263229251

sskasim commented 5 years ago

@marcelgood I am facing this issue with Angular 7 and Breeze and I am at a dead end now.

"breeze-bridge2-angular": "^1.1.0", "breeze-client": "^1.7.2",

Although the 'Authorization' custom header that I add to the ajaxAdapter.defaultSettings.header reaches the web api, the other 'ConnStringName' custom header does not reach the web api.

marcelgood commented 5 years ago

@sskasim I would need a small sample from you that demonstrates the issue, so I can see what might be going on. Very little to go on in your description.

sskasim commented 5 years ago

@marcelgood The issue is that any custom headers that I add to my client app (Angular7/Breeze) either through breeze adapter or through the Angular Http interceptor does not reach my Web API controller methods. Even though I can see those custom headers being sent in the browser's Network tab under Headers section in the details for the ajax call being made. I have CORs enabled on my web api with allow all option. I think it is not an issue with Breeze. Most probably with Web API or server settings related to headers or could be Angular http related. But any help or pointer appreciated. Thanks.

sskasim commented 5 years ago

Sorry, this was an issue in our Web API code. The proxy controller in the DMZ that forwards the Angular http call to the inside controller was white-listing only the Authorization header and throwing away all other headers.