Url modify in angular - url

I have angular application(angular 6) which implemented keycloakAuthentication ,if user is not authenticated angular application redirect user to this service page(third party), when user successfully authenticated he will redirect to our angular website .
Now i am facing issue with http protocol if user type http://portal.cloud.com they will redirect to keycloakAuthentication page but redirect url(http://portal.cloud.com) is invalid because it is authenticating only https url ,how i can modify url in this case means if user type http://portal.cloud.com application will automatically convert it into https://portal.cloud.com and send it to keycloakAuthentication page.
I tried some solution like implemented authguard as below:
import {Injectable, isDevMode} from '#angular/core';
import {CanActivate} from '#angular/router';
#Injectable()
export class IsSecureGuard implements CanActivate {
canActivate(): boolean {
if ((isDevMode()) && (location.protocol !== 'https:')) {
location.href = 'https:' + window.location.href.substring(window.location.protocol.length);
return false;
}
return true;
}
}
but it is not working as expected please suggests some solution to modify url on load.

You can simplify your implementation by resolving the redirect to HTTPS before the user reaches your application, by setting up NGINX to do this for example:
See here how to: https://serverfault.com/questions/67316/in-nginx-how-can-i-rewrite-all-http-requests-to-https-while-maintaining-sub-dom

Related

Spring SAML redirect URL after login which contains # character

I am using Spring SAML for my application and I faced a problem with redirect URL after logged in successfully.
I am trying to save the URL before login to the app.
For example, When I access the link as http://localhost:8080/myapp/#request/123/details, spring-security will redirect to the login page.
I expected that after logged in successfully, the app auto redirect to the above URL. I have configured sucessRedirectHandler by using SavedRequestAwareAuthenticationSuccessHandler.
But, after logging in, the application redirects to https://localhost:8080/myapp/.
I also debugged and saw that the request URL does not contains "#request/123/details" part.
Do you have any ideas for this case?
Thank you.
Have you tried setting SAMLEntryPoint options, which preserves the requested URL as relay state.Check below code
#Bean
public SAMLEntryPoint samlEntryPoint() {
SAMLEntryPoint entryPoint = new SAMLEntryPoint();
entryPoint.setDefaultProfileOptions(defaultWebSSOProfileOptions());
return entryPoint;
}
#Bean
public WebSSOProfileOptions defaultWebSSOProfileOptions() {
WebSSOProfileOptions options = new WebSSOProfileOptions();
options .setBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
options.setRelayState(current-SP-URL);
return options;
}

Angular 5 unable to get XSRF token from HttpXsrfTokenExtractor

I am trying to make a POST request via an absolute URL to a Spring (Basic authentication) secured Rest API.
Having read that Angular omits inserting the X-XSRF-TOKEN into the request header automatically for absolute urls, I tried to implement an HttpInterceptor to add the token in.
In my original /signin POST request, I create the necessary authorization: Basic header to ensure Spring authenticates the request.
The response header returned contains the expected set-cookie token:
Set-Cookie:XSRF-TOKEN=4e4a087b-4184-43de-81b0-e37ef953d755; Path=/
However, in my custom interceptor class when I try to obtain the token from the injected HttpXsrfTokenExtractor for the next request, it returns null.
Here is the code for my interceptor class:
import {Injectable, Inject} from '#angular/core';
import { Observable } from 'rxjs/Rx';
import {HttpInterceptor, HttpXsrfTokenExtractor, HttpRequest, HttpHandler,
HttpEvent} from '#angular/common/http';
#Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {
constructor(private tokenExtractor: HttpXsrfTokenExtractor) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let requestMethod: string = req.method;
requestMethod = requestMethod.toLowerCase();
if (requestMethod && (requestMethod === 'post' || requestMethod === 'delete' || requestMethod === 'put' )) {
const headerName = 'X-XSRF-TOKEN';
let token = this.tokenExtractor.getToken() as string;
if (token !== null && !req.headers.has(headerName)) {
req = req.clone({ headers: req.headers.set(headerName, token) });
}
}
return next.handle(req);
}
}
tokenExtractor.getToken() returns null in the above code. I expected it to return the token from Spring (Set-Cookie) response header of my previous /signin request.
I read this related post for creating the interceptor: angular4 httpclient csrf does not send x-xsrf-token
But I wasn't able to find much documentation for HttpXsrfTokenExtractor other than this:
https://angular.io/api/common/http/HttpXsrfTokenExtractor
Question: Why is HttpXsrfTokenExtractor.getToken() returning null?
In addition I added the interceptor class as a provider to the app.module.
Here is my app.module.ts:
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { LocationStrategy, HashLocationStrategy, APP_BASE_HREF } from '#angular/common';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { HttpClientModule, HttpClientXsrfModule } from '#angular/common/http';
import { HTTP_INTERCEPTORS } from '#angular/common/http';
import { AlertModule } from 'ng2-bootstrap';
import { routing, appRouterProviders } from './app.routing';
import { HttpXsrfInterceptor } from './httpxrsf.interceptor';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './registration/register.component';
import { HomeComponent } from './home/home.component';
#NgModule({
declarations: [AppComponent,
LoginComponent,
RegisterComponent,
HomeComponent],
imports: [BrowserModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
HttpClientXsrfModule, // Adds xsrf support
AlertModule.forRoot(),
routing],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [
appRouterProviders,
[{provide: APP_BASE_HREF, useValue: '/'}],
[{provide: LocationStrategy, useClass: HashLocationStrategy}],
[{provide: HTTP_INTERCEPTORS, useClass: HttpXsrfInterceptor, multi: true }]
],
bootstrap: [AppComponent]
})
export class AppModule {}
Another thing to note is that I am running my Angular front end on Node.js from localhost:3000 and my Spring Rest back-end from localhost:8080.
They are on different ports, and so the reason for making http requests with absolute urls. Would the browser prevent a Set-Cookie working when it comes from a response for a request on a different domain?
Could I be missing anything else?
Thank you for any help.
----------------------------------------------
[Updated 7th Jan 2018]
FIX
I use Webpack dev server to serve the Angular code. I worked around the issue by configuring a proxy for urls that point to my back-end Rest API.
This means all requests made from the browser now only be to the dev server at port 3000, even for the Rest API calls.
When the webpack dev server sees any request urls with the configured pattern (E.g /api/...), it replaces with calls to the back-end server on http://localhost:8080 (in my case).
This is my what I added to the devServer section of my webpack.dev.js file:
proxy: {
'/api': {
'target': 'http://localhost:8080',
'pathRewrite': {'^/api' : ''}
//Omits /api from the actual request. E.g. http://localhost:8080/api/adduser -> http://localhost:8080/adduser
}
}
With this set up, I no longer make cross-domain (cross-origin) requests from the Angular code or use absolute URLs anymore. This hugely simplifies things as I am not fighting the Angular XSRF (CSRF) mechanism anymore. It just works by default. I also do not need to use an HttpInterceptor to manually insert the X-XSRF-TOKEN in either.
The added benefit of setting up a dev server proxy is that client requests are no longer absolute, so I do not need to change all the Rest API calls for production.
I hope this is useful for anyone who is suffering the same problem/understanding.
Webpack dev server proxy documentation ref:
https://webpack.js.org/configuration/dev-server/#devserver-proxy
Since you are using different ports (3000 & 8080), you are making a cross-origin request, so you will not be able to read the cookie in the client sent from the server. If you want to separate your client and server in this way, you need to use a proxy so the client and server applications are served from the same protocol (http/https), domain, and port. If you are using Spring Boot I would suggest you look at Spring Cloud Netflix, specifically Zuul (https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#_router_and_filter_zuul).

Handle url with .html extension in angular2 router?

We are working on a Angular2(created from angular-cli v1.0.0-beta.28.3) which connects to Salesforce via OAth's User-Agent Flow mechanism(https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_understanding_user_agent_oauth_flow.htm).
Here is the authentication code i've used in the code.
Here's the code which handles the callback after Authentication:
import { Component } from '#angular/core';
import { ForceService } from './force';
#Component({
template : ` `,
})
export class AuthComponent {
constructor(force : ForceService) {
var event = new CustomEvent("oauthCallback", {'detail': window.location.href});
window.opener.document.dispatchEvent(event);
window.close();
}
}
In Salesforce, we have something called Connected-App, where we configure the callback urls for the authentication. Callbacks url looks like below:
https://my.site.com/#/auth-callback. Angular2 router configuration uses useHash:true option as well.
Everything is working fine until the new requirement which needs only the callback url to point to something like this:
https://my.site.com/auth-callback.html
So the equivalent html code for above will be :
<html>
<script type="text/javascript">
var event = new CustomEvent("oauthCallback", {'detail': window.location.href});
window.opener.document.dispatchEvent(event);
window.close();
}
</script>
</html>
Problem, we face is after Authenticating, we are not able to access the access_token from the url and close the Authentication Window, since the routing is not properly redirecting to auth-callback.html. All we get is a blank screen with url changed to http://localhost:4200/#/access_token
Is there's a way to solve this issue without turning off useHash ?

How to redirect to login page if AbpAuthorize is failed in asp.net mvc?

I have modified the asp.net boilerplate mvc template css, but somehow, I might mess up with the code. Now if the user is not logged in/not authorized, the error pops out, click ok, instead of redirecting to the login page, it redirects to the default $urlRouterProvider in the app.js. Here is part of the app config:
app.config([
'$stateProvider', '$urlRouterProvider', '$locationProvider', '$qProvider',
function ($stateProvider, $urlRouterProvider, $locationProvider, $qProvider) {
$locationProvider.hashPrefix('');
$urlRouterProvider.otherwise("/");
$qProvider.errorOnUnhandledRejections(false);
$stateProvider
.state('login', {
url: '/Account/Login',
templateUrl:'/Views/Account/Login.cshtml'
})
.state('dashboard', {
url: '/',
templateUrl: '/App/Main/views/dashboard/dashboard.html',
data: { pageTitle: '' }
})
]);
Can any one please explain what happens behind the scenes? What is the magic code that redirects the user to the login page regardless of what you defined in the angularjs?
Thanks in advance!
After days of search, I found out the reason why it did not work is because the response is not handled by HandleUnauthorizedRequest method. It is supposed to redirect to the path you defined in the startup when receiving 401 unauthorized status.
And why it did not trigger the HandleUnauthorizedRequest is because I removed the AbpMvcAuthorize in Home controller.

URL encoding # sharp to Durandal SPA

I know it is not necessary to specify a login URL to a Durandal login page. But I wonder how to fix the following problem I'm facing to redirect a Authentication to a specific Durandal Page that has a Sharp sign (i.e. #).
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
//LoginPath = new PathString("/" + HttpUtility.UrlDecode("#") + "/login")
LoginPath = new PathString("/#/login")
});
...
}
When I paste:
http://localhost/#/login
to a browser URL I can navigate to the login page without any problems. I can login and it is working fine.
Because I'm mixing MVC with SPA in some scenarios, when I add [Authorize] attribute to an MVC controller, then I will be redirected as expected to
http://localhost/%23/login?ReturnUrl=%2Fe
and I get the error:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /#/login
how to use the # sign instead of %23 char encoding? or maybe I'm messing something else!!!
FYI:
My question is related to character encoding not something line this: Login page on different domain because I may face the same problem in other situations in the future.
As far as I know it isn't possible to use a # from the server. A possible solution could be to redirect the user from the shell in durandal.js when the user isn't authenticated.
I don't know if it is too late in the life cycle but you could try to catch the redirect in the global asax file in the Application_EndRequest event and fix the response. I have code like this to change the actionresult to a jsonresult that my js can work with so that when a user attempts to reach an MVC controller resource that I have protected via java script ajax I can handle it properly.
protected void Application_EndRequest()
{
var context = new HttpContextWrapper(this.Context);
// If we're an ajax request and forms authentication caused a 302,
// then we actually need to do a 401
if (FormsAuthentication.IsEnabled && context.Response.StatusCode == 302
&& context.Request.IsAjaxRequest())
{
var jsRet = new JSONFunctionResult<Boolean>();
if (this.Request.IsAuthenticated)
{
jsRet.Messages.Add(new JSONFunctionResultMessage()
{
Text = string.Format("You must have one of these roles to perform the operation: {0}", context.Response.Headers["RolesRequired"]),
Title = "Security Violation",
Type = (int)JSONFunctionResultMessageTypes.authorization
});
}
else
{
jsRet.Messages.Add(new JSONFunctionResultMessage()
{
Text = "You must be logged into to access this resource",
Title = "Security Violation",
Type = (int)JSONFunctionResultMessageTypes.authentication
});
}
jsRet.OperationStatus = JSONFunctionResultOperationStatus.error.ToString();
string jresponse = JsonConvert.SerializeObject(jsRet);
context.Response.Clear();
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
context.Response.Write(jresponse);
}
}
In my code I check for a redirect and if forms auth is enabled and if the user is Authenticated and change the response accordingly. For your code you could maybe change the response to go to your correct login page?

Resources