Azure-Samples / ms-identity-java-webapp

A Java web application calling Microsoft graph that is secured using the Microsoft identity platform
MIT License
118 stars 105 forks source link

redirect issue in spring-security-web-app on Azure App Service (not on localhost) #65

Closed Ranbir-Sinha closed 3 years ago

Ranbir-Sinha commented 3 years ago

I was working on the spring-security-web-app in my localhost. The authentication worked fine and I was able to see the name of the logged-in user. Afterwards, I created an azure app service to host this application. The configuration of app service is Tomcat 9.0.31, Java 8 and HTTPS (TLS1.2). After deploying the web app to Azure app service, I am able to see the login page. When I clicked on the login page, the redirect happened to "http://mywebapp.azurewebsites.net/login" instead of "https://mywebapp.azurewebsites.net/login". Therefore, the sign-in could not happen.

When I manually edit the url to "https://mywebapp.azurewebsites.net/login" and pressed Enter, I am able to authenticate myself but the redirect to "secure_page" is again happening on "http" instead of "https".

On localhost this webapp is working perfectly but not when deployed to app service.

Ranbir-Sinha commented 3 years ago

This problem happens when your Tomcat server is behind a proxy. The HTTPS requests terminate at the proxy and the proxy then uses HTTP protocol to communicate to your Tomcat server.

I solved this error by making the following changes:

In application.properties:

security.oauth2.client.pre-established-redirect-uri=https://yourappurl.net/login
security.oauth2.client.registered-redirect-uri=https://yourappurl.net/login
security.oauth2.client.use-current-uri=false
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto
server.tomcat.use-relative-redirects=true
server.use-forward-headers=true
server.tomcat.internal-proxies=.*

In SpringBootApplication class:

import org.springframework.core.Ordered;
import org.springframework.web.filter.ForwardedHeaderFilter;

@Bean
FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
    final FilterRegistrationBean<ForwardedHeaderFilter> filterRegistrationBean = new FilterRegistrationBean<ForwardedHeaderFilter>();
    filterRegistrationBean.setFilter(new ForwardedHeaderFilter());
    filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return filterRegistrationBean;
}

In AppConfiguration class's configure method:

http.requiresChannel().anyRequest().requiresSecure();

More info can be found here

Avery-Dunn commented 3 years ago

Hello @Ranbir-Sinha : Glad to see you got your issue solved, when I get the chance I'll add your info about proxies to the sample's README (in case anyone else runs into the same issue). Do you have any other issues, or is the sample working for you now?

Ranbir-Sinha commented 3 years ago

Hello @Avery-Dunn, The sample is working perfectly after the changes described in my previous comment.

Now, I am trying a different setup. I call this application the "frontend". There is another webapp (Spring Boot) which acts as a "backend". Both hosted on different Azure App Service. Therefore two app registrations are required. The backend uses bearer token to authenticate. I also want to use the Microsoft Graph API to get info about the user and to perform ROLE authorization in both frontend and backend. The problem I am facing is that when I specify scopes from Microsoft Graph API and my backend webservice (user_impersonation), the frontend receives a token which is valid only for Microsoft Graph api and not for backend. When I set only the scope of backend (user_impersonation), the token does not work for Microsoft Graph API.

Do you have a suggestion on the setup of such architecture?

Additionally, I would like the "backend" to login to the Azure MySQL database (AD Auth enabled) with the token for each logged-in user. Can this impersonation be done in the spring code?

sangonzal commented 3 years ago

@Ranbir-Sinha It seems that what you are looking for here is on-behalf-of flow. This sample shows how to incorporate OBO flow into a Java web application. In your frontend you would acquire tokens for the backend application. You send the request to the backend along with the token in the header, and then you would validate the tokens in the backend, and then use OBO to exchange that token for the Graph API token. You can also use this pattern for the Azure MySQL database.

If you are using Spring, this Azure AD Spring boot starters would also possibly work in your scenario. I would take a look at this sample.