TrilonIO / aspnetcore-angular-universal

ASP.NET Core & Angular Universal advanced starter - PWA w/ server-side rendering for SEO, Bootstrap, i18n internationalization, TypeScript, unit testing, WebAPI REST setup, SignalR, Swagger docs, and more! By @TrilonIO
https://www.trilon.io
MIT License
1.46k stars 433 forks source link

how use ssr rest NTLM auth #268

Closed Gorniv closed 7 years ago

Gorniv commented 7 years ago

I need use NTLM(windows iis auth) in my project. What do I need to do transferHttp work on the server? I need use "{ provide: CookieService, useClass: ServerCookieService }" ? - i dont understand CookieService - imoprt from "?" ?

Adondriel commented 7 years ago

Don't even touch those files. you can look at my fork of this project to see how I got NTLM auth to work, it's REALLY simple! Specifically, this branch https://github.com/Adondriel/aspnetcore-angular2-universal/tree/master-forgit

This branch both has SSR and NTLM enabled, plus a couple of other things were removed that I didn't need. The main changes for NTLM to work were in the startup.cs files main method, I believe.

To get this to work, I had to use WebListener, instead of Kestrel. This means that this will NOT work on anything other than windows, so make sure you are working on a windows machine.

Gorniv commented 7 years ago

When ssr get data from api -401 error. When page loaded, browser load data from api- with Ok(200).

fail: Microsoft.AspNetCore.NodeServices[0]
      ERROR Response {
        _body: '',
        status: 401,
        ok: false,
        statusText: 'Unauthorized',
        headers:
         Headers {
           _headers:
            Map {
              'content-length' => [Object],
              'server' => [Object],
              'www-authenticate' => [Object],
              'date' => [Object] },
           _normalizedNames:
            Map {
              'content-length' => 'content-length',
              'server' => 'server',
              'www-authenticate' => 'www-authenticate',
              'date' => 'date' } },
        type: 2,
        url: 'http://localhost:5000/api/users' }
info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1]

get:

return this.transferHttp.get("${this.baseUrl}/api/users", { withCredentials: true });

source page:

 <body>
        <app ng-version="4.1.3"><div class="container-fluid">
  <div class="row">
    <div class="col-md-4 col-lg-3 collapse in show" style="overflow:visible;height:auto;display:block;" aria-expanded="true" aria-hidden="false">
      <button class="btn btn-primary" type="button">
        <span class="fa fa-bars"></span>
    </button>
      <nav-menu _nghost-c1=""><div _ngcontent-c1="" class="bootstrap-vertical-nav">
    <div _ngcontent-c1="" class="card card-block card-header">
        <ul _ngcontent-c1="" class="nav flex-column">
            <li _ngcontent-c1="">
                <a _ngcontent-c1="" class="nav-link" href="/home"><span _ngcontent-c1="" class="fa fa-home"></span>Home</a>
            </li>
            <li _ngcontent-c1="" class="link-active">
                <a _ngcontent-c1="" class="nav-link" href="/staff"><span _ngcontent-c1="" class="fa fa-user"></span>Staff &amp; Rate Management</a>
            </li>
        </ul>
    </div>

</div></nav-menu>
    </div>
    <div class="col-md-8 col-lg-9" className="col-md-8 col-lg-9">
      <div class="collapse" style="display:none;" aria-expanded="false" aria-hidden="true">
        <button class="btn btn-primary" type="button">
          <span class="fa fa-bars"></span>
      </button>
      </div>
      <router-outlet></router-outlet><staff _nghost-c2=""><h1 _ngcontent-c2=""> This is the Staff Page </h1>
</staff>
    </div>
  </div>
</div>
</app><script>window['TRANSFER_STATE'] = {}</script>

        <!-- remove if you're not going to use SignalR -->
        <script src="https://code.jquery.com/jquery-2.2.4.min.js"
                integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
                crossorigin="anonymous"></script>

        <script src="http://ajax.aspnetcdn.com/ajax/signalr/jquery.signalr-2.2.0.min.js"></script>

        <!-- Here we're passing down any data to be used by grabbed and parsed by Angular -->
        <script>window['TRANSFER_CACHE'] = {"someData":"Transfer this to the client on the window.TRANSFER_CACHE {} object","fromDotnet":"Hi Angular it's asp.net :)"};</script>

    <!-- Our webpack bundle -->
    <script src="/dist/main-browser.js" asp-append-version="true"></script>

    </body>
Adondriel commented 7 years ago

then your ntlm isnt setup correctly,.

Adondriel commented 7 years ago

oh, in your web.config, try making "forwardWindowsAuth" to true

Gorniv commented 7 years ago

my fork for test ntlm forwardWindowsAuth =true - but not working

Adondriel commented 7 years ago

make sure your systems are configured for ntlm, i cant really help much in that area, though. is it giving the same error?

Gorniv commented 7 years ago

ntlm work with browser. But when ssr send request like: return this.transferHttp.get("${this.baseUrl}/api/users", { withCredentials: true }); Credentials not working, because Credentials working by browser.

Adondriel commented 7 years ago

the transferHttp, should be sending with credentials anyway... if you add the [authorize] tag to the method, or class it will force credentials. You should not need to manage the credentials on the client side at all. Not sure why you are adding the "withCredentials" options, because if you setup the backend correctly, you shouldn't need it.

Adondriel commented 7 years ago

Also, in the example it says to not use transferHttp, due to that exact issue. transferHttp is used as a way to allow the SSR to pre-render/pre-load results on page load, instead of having to wait till the page loads (I ended up not using this, personally because I don't feel it benefits my app, I ended up turning off SSR.)

Gorniv commented 7 years ago

i use cookie for access_token main.server.ts

import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';

ROUTES.forEach(route => {
  app.get(route, (req, res) => {
    // tslint:disable-next-line:no-console
    console.time(`GET: ${req.originalUrl}`);
    res.render('../dist/index', {
      req,
      res,
      providers: [
        {
          provide: REQUEST, useValue: (req)
        },
        {
          provide: RESPONSE, useValue: (res)
        }
      ]
    });
    // tslint:disable-next-line:no-console
    console.timeEnd(`GET: ${req.originalUrl}`);
  });
});

app.listen(8000, () => {
  // tslint:disable-next-line:no-console
  console.log(`Listening at ${baseUrl}`);
});

and then on server side i use

        let cookiesString = this.request.headers['cookie'];