Just for some background, I have been trying to set up Azure AD OAuth authentication using flask dance. The actual issue I'm having is when redirecting to the authorization URL, flask is not redirecting as I would expect it to.
I've tried using a simple redirect like this:
@oauth_blueprint.route('/')
def route_oauth():
return flask.redirect('https://login.microsoftonline.com/tenant/oauth2/v2.0/authorize')
Now, if I access the page directly at localhost:5000/oauth
then it works as expected and redirects me to the URL.
However, our web application is set up using a reverse proxy. The flask application is accessed at www.example.com/flask/xxx
which returns the corresponding page at localhost:5000/xxx
. So if I go to www.example.com/flask/oauth
then I should get the same page as localhost:5000/oauth
.
So the problem is when I go to www.example.com/flask/oauth
the redirect does not work correctly. Instead of taking me to https://login.microsoftonline.com/tenant/oauth2/v2.0/authorize
, I instead get redirected to www.example.com/tenant/oauth2/v2.0/authorize
. I can't make sense of this as the URL is clearly not a relative URL.
In case it's relevant, we are using URL Rewrite in IIS. There is a simple rewrite rule which matches any URL starting with flask/
. This has worked fine for all our other pages.
This is what the rule looks like in IIS:
I've tried using FRT to trace the rewrite. Below is the output I got. It changes to the undesired URL at line 27 but I don't understand why.
| No. | EventName | Details | Time |
| --- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
| 1. | GENERAL_REQUEST_START | SiteId="2", AppPoolId="App", ConnId="1610613017", RawConnId="1610613017", RequestURL="http://www.example.com/flask/oauth/", RequestVerb="GET" | 12:35:02.963 |
| 2. | GENERAL_ENDPOINT_INFORMATION | RemoteAddress="::1", RemotePort="64068", LocalAddress="::1", LocalPort="81" | 12:35:02.963 |
| 3. | GENERAL_REQUEST_HEADERS | Headers="Connection: keep-alive Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,\*/\*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Host: www.example.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.82 sec-ch-ua: "Not.A/Brand";v="8", "Chromium";v="114", "Microsoft Edge";v="114" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" Upgrade-Insecure-Requests: 1 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document " | 12:35:02.963 |
| 4. | GENERAL_GET_URL_METADATA | PhysicalPath="", AccessPerms="513" | 12:35:02.963 |
| 5. | HANDLER_CHANGED | OldHandlerName="", NewHandlerName="StaticFile", NewHandlerModules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule", NewHandlerScriptProcessor="", NewHandlerType="" | 12:35:02.963 |
| 6. | URL_REWRITE_START | RequestURL="/flask/oauth/", Scope="Distributed", Type="Inbound" | 12:35:02.963 |
| 7. | RULE_EVALUATION_START | RuleName="Flask", RequestURL="flask/oauth/", QueryString="", PatternSyntax="Regex", StopProcessing="true", RelativePath="/" | 12:35:02.963 |
| 8. | PATTERN_MATCH | Pattern="^flask/(.\*)", Input="flask/oauth/", Negate="false", Matched="true" | 12:35:02.963 |
| 9. | REWRITE_ACTION | Substitution="http://localhost:5000/{R:1}", RewriteURL="http://localhost:5000/oauth/", AppendQueryString="true", LogRewrittenURL="true" | 12:35:02.963 |
| 10. | RULE_EVALUATION_END | RuleName="Flask", RequestURL="http://localhost:5000/oauth/", QueryString="", StopProcessing="true", Succeeded="true" | 12:35:02.963 |
| 11. | GENERAL_SET_REQUEST_HEADER | HeaderName="X-Original-URL", HeaderValue="/flask/oauth/", Replace="true" | 12:35:02.963 |
| 12. | URL_CHANGED | OldUrl="/flask/oauth/", NewUrl="http://localhost:5000/oauth/" | 12:35:02.963 |
| 13. | URL_REWRITE_END | RequestURL="http://localhost:5000/oauth/" | 12:35:02.963 |
| 14. | USER_SET | AuthType="", UserName="", SupportsIsInRole="true" | 12:35:02.963 |
| 15. | HANDLER_CHANGED | OldHandlerName="StaticFile", NewHandlerName="ApplicationRequestRoutingHandler", NewHandlerModules="ApplicationRequestRouting", NewHandlerScriptProcessor="", NewHandlerType="" | 12:35:02.963 |
| 16. | GENERAL_SET_REQUEST_HEADER | HeaderName="Max-Forwards", HeaderValue="10", Replace="true" | 12:35:02.963 |
| 17. | GENERAL_SET_REQUEST_HEADER | HeaderName="X-Forwarded-For", HeaderValue="[::1]", Replace="true" | 12:35:02.963 |
| 18. | GENERAL_SET_REQUEST_HEADER | HeaderName="X-ARR-SSL", HeaderValue="", Replace="true" | 12:35:02.963 |
| 19. | GENERAL_SET_REQUEST_HEADER | HeaderName="X-ARR-ClientCert", HeaderValue="", Replace="true" | 12:35:02.963 |
| 20. | GENERAL_SET_REQUEST_HEADER | HeaderName="X-ARR-LOG-ID", HeaderValue="0f2e2610-c6a8-4412-be80-431075354701", Replace="true" | 12:35:02.963 |
| 21. | GENERAL_SET_REQUEST_HEADER | HeaderName="Connection", HeaderValue="", Replace="true" | 12:35:02.963 |
| 22. | URL_CHANGED | OldUrl="http://localhost:5000/oauth/", NewUrl="/flask/oauth/" | 12:35:02.963 |
| 23. | GENERAL_SET_RESPONSE_HEADER | HeaderName="Content-Length", HeaderValue="371", Replace="true" | 12:35:02.963 |
| 24. | GENERAL_SET_RESPONSE_HEADER | HeaderName="Content-Type", HeaderValue="text/html; charset=utf-8", Replace="true" | 12:35:02.963 |
| 25. | GENERAL_SET_RESPONSE_HEADER | HeaderName="Location", HeaderValue="https://login.microsoftonline.com/tenant/oauth2/v2.0/authorize", Replace="true" | 12:35:02.963 |
| 26. | GENERAL_SET_RESPONSE_HEADER | HeaderName="Access-Control-Allow-Origin", HeaderValue="/..", Replace="false" | 12:35:02.963 |
| 27. | GENERAL_SET_RESPONSE_HEADER | HeaderName="Location", HeaderValue="http://www.example.com/tenant/oauth2/v2.0/authorize", Replace="true" | 12:35:02.963 |
| 28. | GENERAL_SET_RESPONSE_HEADER | HeaderName="X-Powered-By", HeaderValue="ARR/3.0", Replace="false" | 12:35:02.963 |
| 29. | GENERAL_NOT_SEND_CUSTOM_ERROR | Reason="SETSTATUS_SUCCESS" | 12:35:02.963 |
| 30. | GENERAL_FLUSH_RESPONSE_START | | 12:35:02.963 |
| 31. | GENERAL_RESPONSE_HEADERS | Headers="Content-Type: text/html; charset=utf-8 Location: http://www.example.com/tenant/oauth2/v2.0/authorize Server: Microsoft-IIS/10.0 Access-Control-Allow-Origin: /.. X-Powered-By: ARR/3.0 " | 12:35:02.963 |
| 32. | GENERAL_RESPONSE_ENTITY_BUFFER | Buffer="<!doctype html> <html lang=en> <title>Redirecting...</title> <h1>Redirecting...</h1> <p>You should be redirected automatically to the target URL: <a href="https://login.microsoftonline.com/tenant/oauth2/v2.0/authorize">https://login.microsoftonline.com/tenant/oauth2/v2.0/authorize</a>. If not, click the link. " | 12:35:02.963 |
| 33. | GENERAL_FLUSH_RESPONSE_END | BytesSent="666", ErrorCode="The operation completed successfully. (0x0)" | 12:35:02.963 |
| 34. | GENERAL_REQUEST_END | BytesSent="666", BytesReceived="1028", HttpStatus="302", HttpSubStatus="0" | 12:35:02.963 |
I'd really appreciate any help on this issue. I'm happy to provide any more details if necessary. Thank you.
It turns out that this is a feature of Application Request Routing for IIS. Unchecking "Reverse rewrite host in response headers" under "Server Proxy Settings" resolved the issue.