Base solution for your next web application
Open Closed

Open an angular page from the login page without logging in #2207


User avatar
0
george created

Hi,

I want to open a page without logging in to the application. The link to open the page is given in the login page. So, I've created an ActionResult in the account controller like this:

public ActionResult GotoQuickPay()
        {
            return Json(new AjaxResponse { TargetUrl = "/Application#/tenant/quickPay" });
        }

The route for the specified page is also created without checking any permissions:

//Route for Quick Payment
        $stateProvider.state('tenant.quickPay', {
            url: '/quickPay',
            templateUrl: '~/App/tenant/views/onlinePayment/quickPay.cshtml'
        });

But it's showing an error 500. Details:

This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.
InvalidOperationException: This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet. STACK TRACE: at Abp.Web.Mvc.Controllers.Results.AbpJsonResult.ExecuteResult(ControllerContext context) in D:\Halil\GitHub\aspnetboilerplate\src\Abp.Web.Mvc\Web\Mvc\Controllers\Results\AbpJsonResult.cs:line 62 at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult)

If I set JsonRequestBehavior to AllowGet, will it be solved? And is that a good practice?? Any other Solutions???

(Anyways I don't know how to set JsonRequestBehavior to AllowGet :lol: )


9 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    I'm not sure if it will work or not but you can do it like this,

    return Json(new AjaxResponse { TargetUrl = "/Application#/tenant/quickPay" }, JsonRequestBehavior.AllowGet);
    
  • User Avatar
    0
    george created

    <cite>ismcagdas: </cite> Hi,

    I'm not sure if it will work or not but you can do it like this,

    return Json(new AjaxResponse { TargetUrl = "/Application#/tenant/quickPay" }, JsonRequestBehavior.AllowGet);
    

    Exactly it is not worked!! :(

    Now it is returning only the following json in the browser:

    {"result":null,"targetUrl":"/Application#/tenant/quickPay","success":true,"error":null,"unAuthorizedRequest":false,"__abp":true}
    

    Also my URL is changed to "http://localhost:6240/Account/GotoQuickPay"
    Actually, GotoQuickPay is the name of my action result in the controller :| Who could solve this out??

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Why don't you put a link on your login page like this ?

    <a href="/Application#/tenant/quickPay">Quick Pay<a/>
    

    But, since ApplicationController requires authorization, probably you will not be able to see this page without logging in the application.

    You can create a seperate SPA or you can check the url in Index action of ApplicationController and allow only anonymous users if url is quickpay url. You also need to remove AbpMvcAuthorize from your ApplicationController for that.

  • User Avatar
    0
    george created

    Hi, I've given the link like you suggested. And removed the AbpMvcAuthorize from ApplicationController. Then I've got so many errors regarding the chatbar and userdetails pane etc. So, I've added another layout page and removed the rendering of header and sidebar. Then called the new layout in the ApplicationController. Now the errors are reduced to only one. The stacktrace of the same is given below:

    "AbpAuthorizationException: Current user did not login to the application!
    STACK TRACE:    at Abp.Authorization.AuthorizationHelper.<AuthorizeAsync>d__19.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Abp.Authorization.AuthorizationHelper.<CheckPermissions>d__22.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Abp.Authorization.AuthorizationHelper.<AuthorizeAsync>d__20.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Nito.AsyncEx.AsyncContext.<>c__DisplayClass3.<Run>b__1(Task t)
       at System.Threading.Tasks.ContinuationTaskFromTask.InnerInvoke()
       at System.Threading.Tasks.Task.Execute()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Nito.AsyncEx.AsyncContext.Run(Func`1 action)
       at Abp.Authorization.AuthorizationHelperExtensions.Authorize(IAuthorizationHelper authorizationHelper, MethodInfo methodInfo)
       at Abp.Authorization.AuthorizationInterceptor.Intercept(IInvocation invocation)
       at Castle.DynamicProxy.AbstractInvocation.Proceed()
       at Abp.Domain.Uow.UnitOfWorkInterceptor.PerformAsyncUow(IInvocation invocation, UnitOfWorkOptions options)
       at Abp.Domain.Uow.UnitOfWorkInterceptor.PerformUow(IInvocation invocation, UnitOfWorkOptions options)
       at Abp.Domain.Uow.UnitOfWorkInterceptor.Intercept(IInvocation invocation)
       at Castle.DynamicProxy.AbstractInvocation.Proceed()
       at Abp.Auditing.AuditingInterceptor.Intercept(IInvocation invocation)
       at Castle.DynamicProxy.AbstractInvocation.Proceed()
       at Abp.Runtime.Validation.Interception.ValidationInterceptor.Intercept(IInvocation invocation)
       at Castle.DynamicProxy.AbstractInvocation.Proceed()
       at Castle.Proxies.SessionAppServiceProxy.GetCurrentLoginInformations()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
       at Abp.WebApi.Controllers.Dynamic.Interceptors.AbpDynamicApiControllerInterceptor`1.Intercept(IInvocation invocation)
       at Castle.DynamicProxy.AbstractInvocation.Proceed()
       at Castle.Proxies.DynamicApiController`1Proxy_2.GetCurrentLoginInformations()
       at lambda_method(Closure , Object , Object[] )
       at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.&lt;&gt;c__DisplayClass12.&lt;GetExecutor&gt;b__8(Object instance, Object[] methodParameters)
       at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
       at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Abp.WebApi.Uow.AbpApiUowFilter.<ExecuteActionFilterAsync>d__5.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Abp.WebApi.Validation.AbpApiValidationFilter.<ExecuteActionFilterAsync>d__5.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Abp.WebApi.Auditing.AbpApiAuditFilter.<ExecuteActionFilterAsync>d__4.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Abp.WebApi.Security.AntiForgery.AbpAntiForgeryApiFilter.<ExecuteAuthorizationFilterAsync>d__10.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Abp.WebApi.Authorization.AbpApiAuthorizeFilter.<ExecuteAuthorizationFilterAsync>d__7.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Web.Http.Controllers.AuthenticationFilterResult.<ExecuteAsync>d__0.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()
    "
    

    I've removed the possible checking of the current user from layout page. Also nothing is there in my quick-pay page too.

    What do you meant by creating a separate SPA?? Is that creating another angular module in app.js? And how can I check the url in Index action of ApplicationController?? There is no parameter is passed to this controller. Then how can I identify whether I'm going to load a page for an anonymous user?

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Actually you will need something like a app.js. In your Controller, you can check Request.Url parameter and decide according to it's value.

  • User Avatar
    0
    george created

    Hi, I've created another SPA named 'appAnonymous'. appForAnonymous.js

    /* 'appAnonymous' MODULE DEFINITION */
    var appModuleAnonymous = angular.module("appAnonymous", [
        "ui.router",
        "ui.bootstrap",
        'ui.utils',
        "ui.jq",
        "oc.lazyLoad",
        "ngSanitize",
        'angularMoment',
        'frapontillo.bootstrap-switch',
        'abp'
    ]);
    
    /* LAZY LOAD CONFIG */
    
    /* This application does not define any lazy-load yet but you can use $ocLazyLoad to define and lazy-load js/css files.
     * This code configures $ocLazyLoad plug-in for this application.
     * See it's documents for more information: https://github.com/ocombe/ocLazyLoad
     */
    appModuleAnonymous.config(['$ocLazyLoadProvider', function ($ocLazyLoadProvider) {
        $ocLazyLoadProvider.config({
            cssFilesInsertBefore: 'ng_load_plugins_before', // load the css files before a LINK element with this ID.
            debug: false,
            events: true,
            modules: [],
    
        });
    }]);
    
    /* THEME SETTINGS */
    App.setAssetsPath(abp.appPath + 'metronic/assets/');
    appModuleAnonymous.factory('settings', ['$rootScope', function ($rootScope) {
        var settings = {
            layout: {
                pageSidebarClosed: false, // sidebar menu state
                pageContentWhite: true, // set page content layout
                pageBodySolid: false, // solid body color state
                pageAutoScrollOnLoad: 1000 // auto scroll to top on page load
            },
            layoutImgPath: App.getAssetsPath() + 'admin/layout4/img/',
            layoutCssPath: App.getAssetsPath() + 'admin/layout4/css/',
            assetsPath: abp.appPath + 'metronic/assets',
            globalPath: abp.appPath + 'metronic/assets/global',
            layoutPath: abp.appPath + 'metronic/assets/layouts/layout4'
        };
    
        $rootScope.settings = settings;
    
        return settings;
    }]);
    
    /* ROUTE DEFINITIONS */
    
    appModuleAnonymous.config([
        '$stateProvider', '$urlRouterProvider',
        function ($stateProvider, $urlRouterProvider) {
                    
            //Route for Quick Payment
            $stateProvider.state('tenant.quickPay', {
                url: '/quickPay',
                templateUrl: '~/App/tenant/views/onlinePayment/quickPay.cshtml'
            });
            
    
            //Route for Fee Payment Receipt
                $stateProvider.state('tenant.paymentReceiptAnonymous', {
                    url: '/paymentReceipt',
                    templateUrl: '~/App/tenant/views/onlinePayment/paymentReceipt.cshtml'
                });                      
        }
    ]);
    
    appModuleAnonymous.run(["$rootScope", "settings", "$state", function ($rootScope, settings, $state) {
        $rootScope.$state = $state;
        $rootScope.$settings = settings;
          
            
        $rootScope.safeApply = function (fn) {
            var phase = this.$root.$$phase;
            if (phase == '$apply' || phase == '$digest') {
                if (fn && (typeof (fn) === 'function')) {
                    fn();
                }
            } else {
                this.$apply(fn);
            }
        };
    }]);
    

    Then given the new module name in my quickpay page and layoutForAnonymous page controllers.

    layoutForAnonymous.cshtml

    <!DOCTYPE html>
    
    
    
    <html lang="en" data-ng-app="appAnonymous" dir=@(CultureHelper.IsRtl ? "rtl" : "")>
    
    <head>
        <title>Smart_campus</title>
        <meta charset="utf-8" />
    
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta content="width=device-width, initial-scale=1" name="viewport" />
        <meta content="" name="description" />
        <meta content="" name="author" />
    
        <link rel="shortcut icon" href="~/favicon.ico" />
    
        <link href="~/Content/fonts-open-sans.css" rel="stylesheet" type="text/css" />
    
        @Styles.Render("~/Bundles/App/libs/css" + (CultureHelper.IsRtl ? "RTL" : ""))
        <link id="ng_load_plugins_before" />
        @Styles.Render("~/Bundles/App/metronic/css" + (CultureHelper.IsRtl ? "RTL" : ""))
        @Styles.Render("~/Bundles/Common/css")
        @Styles.Render("~/Bundles/App/css")
        <link rel="stylesheet" href="~/Common/Styles/CommonStyles.css" type="text/css" />
    
    
        <script type="text/javascript">
            @* This is used to get the application's root path from javascript.
            It's useful if you're running application in a virtual directory under IIS. *@
            var abp = abp || {}; abp.appPath = '@ApplicationPath';                
        </script>
        
    </head>
    
    
    
    
    
    
    
    
    
    
    
    <body ng-controller="common.views.layoutForAnonymous as vm" class="page-md page-header-fixed page-sidebar-closed-hide-logo page-container-bg-solid page-sidebar-closed-hide-logo page-on-load" ng-class="{'page-sidebar-closed': settings.layout.pageSidebarClosed}">
    
        <div ng-spinner-bar class="page-spinner-bar">
            <div class="bounce1"></div>
            <div class="bounce2"></div>
            <div class="bounce3"></div>
        </div>
        
        <div class="clearfix">
        </div>
        
        <div class="page-container">
            <div class="page-content-wrapper">
                <div class="page-content">
                    <div ui-view class="fade-in-up">
                    </div>
                </div>
            </div>       
        </div>
    
        <div class="page-footer-inner">
            <span class="text-muted">
                <small>
                    {{vm.getProductNameWithEdition()}} <br />
                    v@(AppVersionHelper.Version) [@AppVersionHelper.ReleaseDate.ToString("yyyyMMdd")]
                </small>
            </span>
        </div>
    
        <div class="scroll-to-top">
            <i class="icon-arrow-up"></i>
        </div>
    
        
        @Scripts.Render("~/Bundles/App/libs/js")    
    
        <script>
            abp.localization.defaultSourceName = '@LocalizationSourceName';
            moment.locale('@Thread.CurrentThread.CurrentUICulture.Name'); //Localizing moment.js
        </script>
    
        @Html.IncludeScript(ScriptPaths.Angular_Localization)
        @Html.IncludeScript(ScriptPaths.Bootstrap_Select_Localization)
        @Html.IncludeScript(ScriptPaths.JQuery_Timeago_Localization)
    
        
        <script src="~/api/AbpServiceProxies/GetAll?type=angular&v=@(Clock.Now.Ticks)"></script>
        <script src="~/api/AbpServiceProxies/GetAll?v=@(Clock.Now.Ticks)"></script>
        <script src="~/AbpScripts/GetScripts?v=@(Clock.Now.Ticks)" type="text/javascript"></script>
    
        
        <script src="~/signalr/hubs"></script>
        @Html.IncludeScript("~/Abp/Framework/scripts/libs/abp.signalr.js")
    
        @Scripts.Render("~/Bundles/App/metronic/js")    
    
        @Scripts.Render("~/Bundles/Common/js")
        @Scripts.Render("~/Bundles/App/js")    
        <script src="~/App/appForAnonymous.js" type="text/javascript"></script>
        <script src="~/App/common/views/layout/layoutForAnonymous.js" type="text/javascript"></script>
        <script src="~/App/common/views/layout/footer.js" type="text/javascript"></script>
    </body>
    </html>
    

    And added the following in my ApplicationController:

    if (Request.Url.LocalPath == "/Application#/tenant/quickPay")
                {
                    return View("~/App/common/views/layout/layoutForAnonymous.cshtml"); //Layout of quick pay.
                }
                else
                {
                    return View("~/App/common/views/layout/layout.cshtml"); //Layout of the angular application.
                }
    

    Now, I have 2 problems.

    1. In this controller, I've used the 'Request.Url' to identify whether it is coming from the quick pay link or login link. But every time the 'Request.Url' returns the same value "/Application" :( Then how can i decide the layout to be loaded from this url? (I've checked all the properties of 'Request' to find anything like 'quickPay'. Butno luck! :cry: )

    2. Finally, I ran the code manually to load the 'layoutForAnonymous.cshtml'. Got some errors and I've cleared all that. But still the quick pay page is not loading. I can see only the 'ng-spinner-bar' is loading. :roll: The wonder is, no errors are thrown. I could do something if any errors were shown.

    What is I've missed out??

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi George,

    I didn't know that you cannot reach the part after # in the url using Request.Url. You can use your old approach for that, create a paymentController and redirect to necessary cshtml in it.

    For your second problem, probably your new single page app does not contain directives.js file under App/Common/directives/metronic/directives.js into your new SPA app folder.

    Anyway, I have created a sample app for you which has a quick pay link in the login page and redirects you to empty (contains only a text) new spa quick payment page. I have send it to you by email.

    You can take a look at it and use it in your real project,

    Have a nice weekend.

  • User Avatar
    0
    george created

    Hi Ismail, Thank you very much for your lovely help :) I've got your sample code. But unfortunately i couldn't run that in my machine. Because it's showing a number of errors in abp.d TypeScript file. Actually I couldn't find such a file in my project. I think you sent me the latest NetZero project. I'm using the MVC5-Angular Version of ASPNetZero.

    However, I made it by referring your code :lol: I've done the same things seen from your sample code in my project. Finally my quickpay page is displayed without a log in :D Thanks again for your nice support.

    Once again I need your help. That is, my quick pay page is displayed and i can make the payment. But, this quick pay is also tenant specific. I need to Identify the tenant in the quick pay also. We are planning to host the app in azure and create sub-domains for each tenant. Hope it will be possible(tell me if there is any known issues. bcz we are doing this for the first time.) So, please guide me to get the tenantId in the quick Pay.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    I'm glad that you managed to do it :). If you are going to use subdomains, AbpSession.TenantId must be set when you enter a page with tenancyName subdomain.

    You can set website address when you login as host and go to general settings tab. Your website address should contain {TENANCY_NAME} placeholder. An example would be, https://{TENANCY_NAME}.yourcompany.com.

    So, if you go to page <a class="postlink" href="https://tenant2.yourcompany.com">https://tenant2.yourcompany.com</a>, AbpSession.TenantId must be set as far as I remember. Please write back it it is not set.

    You can create a test environment in your local machine by playing your host file and in that way you can test this scenario locally.