Grafana OAuth with keyclock and flask - flask

I am trying to make the Keyclock SSO work with the webapp and Grafana which is embedded in.
I have made the grafana integerate with the keyclock and I am able to login using the keyclock into grafana. But when I embed the grafana as inframe into the webapp, and log into the webapp with keyclock, am shown the error as
login.OAuthLogin(missing saved state)
This is my flask config
{
"web": {
"issuer": "http://localhost:8080/realms/internal",
"auth_uri": "http://localhost:8080/realms/internal/protocol/openid-connect/auth",
"client_id": "flask",
"client_secret": "nlY4o3kIrReiwwsYo0FrKFDHIZvfdXd5",
"redirect_uris": [
"http://localhost:5000/*"
],
"token_uri": "http://localhost:8080/realms/internal/protocol/openid-connect/token",
"token_introspection_uri": "http://localhost:8080/auth/realms/internal/protocol/openid-connect/token/introspect",
"userinfo_uri": "http://localhost:8080/realms/internal/protocol/openid-connect/userinfo"
}
}
Following is my grafana config
[auth.generic_oauth]
enabled = true
name = OAuth
allow_sign_up = true
client_id = grafana
client_secret = CA6OIr8z9v3ZPY4yhPWMSwZWJIPWaRK7
scopes = openid email profile
;email_attribute_name = admin#test.com
;email_attribute_path = admin#test.com
auth_url = http://localhost:8080/realms/internal/protocol/openid-connect/auth
token_url = http://localhost:8080/realms/internal/protocol/openid-connect/token
api_url = http://localhost:8080/realms/internal/protocol/openid-connect/userinfo
;allowed_domains =
;team_ids =
;allowed_organizations =
role_attribute_path = "contains(roles[*], 'Admin') && 'Admin' || contains(roles[*], 'Editor') && 'Editor' || 'Viewer'"
;tls_skip_verify_insecure = false
;tls_client_cert =
;tls_client_key =
;tls_client_ca =
I have the following settings for the cookies and embedding
cookie_samesite = lax
allow_embedding = true
I am getting an 500 Error auth to the grafana is redirected

SameSite=Lax:
Only send the cookie in a first-party context (meaning the URL in the address bar matches the cookie domain). Do not send it with the following cross-origin requests: non-GET, AJAX, iframe, image requests etc. It saves the user from cross-site request forgery.
So Lax is blocking cookie "propagation" to iframe in your use case. None is better option in this case for cookie_samesite Grafana config.

Related

Is it possible to make authentication in identityserver4 only available via REST calls?

I'm quite new with identityserver4 so correct me if I say something wrong. I have set up identityserver4 together with ASP Identity for usermanagement and protected my API with it, however I don't know how to get an access token without having to be redirected to the login page. I'm using postman to get an access token via the authorization tab using the following details:
new Client
{
ClientId = "postman-api",
ClientName = "Postman Test Client",
ClientSecrets = { new Secret("PostmanIsASecret".Sha256()) },
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RequireConsent = false,
RedirectUris = { "https://www.getpostman.com/oauth2/callback"},
PostLogoutRedirectUris = { "https://www.getpostman.com" },
AllowedCorsOrigins = { "https://www.getpostman.com" },
EnableLocalLogin = false,
RequirePkce = false,
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"jumsum.api"
}
}
I just want to login and get an access token without having to be redirected all the time. In the console I'm getting this:
IdentityServer4.ResponseHandling.AuthorizeInteractionResponseGenerator: Information: Showing login: User is not authenticated
I just want to pass a username and password via the http request body and get an access token back. What am I doing wrong?
You could add a client that accepts the client credentials flow and using this flow you can get an access token using just a username and password. This is a flow for machine-to-machine communication where no human user is involved.
Read more about that here

Problem using authorization_code on deployed IdentityServer4 for a PowerBI Extension. What am I missing?

This one has been driving me mad for a couple of days and I can't find a solution. Can someone point me towards one? Thank you in advance!
What I want:
I want to successfully use the authorization_code flow for a Power BI Extension.
What I've done:
I've setup an OAuth2 flow in Power Query M and when I target my localhost IdentityServer, I can login with the user login screen and I get an Access Token that Power BI can use. This works, but it doesn't work on a published IdentityServer (same code, same database). So what am I missing?
Here's what happens:
I have this Power Query M code (it's pretty standard, I found it online and it works on localhost):
StartLogin = (resourceUrl, state, display) =>
let
authorizeUrl = oauthUrl & "connect/authorize?" & Uri.BuildQueryString([
response_type = "code",
client_id = client_id,
redirect_uri = redirect_uri,
state = state,
scope="myscope"
])
in
[
LoginUri = authorizeUrl,
CallbackUri = redirect_uri,
WindowHeight = windowHeight,
WindowWidth = windowWidth,
Context = null
];
FinishLogin = (context, callbackUri, state) =>
let
// parse the full callbackUri, and extract the Query string
parts = Uri.Parts(callbackUri)[Query],
// if the query string contains an "error" field, raise an error
// otherwise call TokenMethod to exchange our code for an access_token
result = if (Record.HasFields(parts, {"error", "error_description"})) then
error Error.Record(parts[error], parts[error_description], parts)
else
TokenMethod("authorization_code", "code", parts[code])
in
result;
TokenMethod = (grantType, tokenField, code) =>
let
queryString = [
grant_type = grantType,
redirect_uri = redirect_uri,
client_id = client_id,
client_secret = client_secret
],
queryWithCode = Record.AddField(queryString, tokenField, code),
tokenResponse = Web.Contents(oauthUrl & "connect/token", [
Content = Text.ToBinary(Uri.BuildQueryString(queryWithCode)),
Headers = [
#"Content-type" = "application/x-www-form-urlencoded",
#"Accept" = "application/json"],
ManualStatusHandling = {400}
]),
body = Json.Document(tokenResponse),
result = if (Record.HasFields(body , {"error", "error_description"})) then
error Error.Record(body[error], body[error_description], body)
else
body
in
result;
What happens is that an IE frame pops up to prompt for my credentials:
The normal login page
Once I login on localhost, I get my AccessToken, life's good :-)
However, once I login on the deployed IdentityServer, I get this one, a 400:
Failing on deployed IdentityServer
The address is: res://ieframe.dll/http_400_webOC.htm#https://identityserverurl/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fresponse_type%3Dcode%26client_id%3DPowerBI%26redirect_uri%3Dhttps%253A%252F%252Foauth.powerbi.com%252Fviews%252Foauthredirect.html%26state%3D852bddfc-638c-44f6-90be-a04346908753%26scope%3Dmyscope
I think the problem has something to do with Internet Explorer and the way the window is being presented, but I can't figure out why it DOES work on localhost, just NOT on a deployed server. The client settings are identical on both localhost and on the deployed IdentityServer (it's on a develop server; they target the same database).
I know on a deployed IdentityServer the home page is not available, so the "https://identityserverurl/" will give a 404. Is something similar going on here?
What am I missing?
Any help is much appreciated! It's the final step to getting it all to work.
Best regards,
Remco
Update: Added IdentityServer logging and Fiddler Trace:
GET /connect/authorize?response_type=code&client_id=PowerBI&redirect_uri=https%3A%2F%2Foauth.powerbi.com%2Fviews%2Foauthredirect.html&state=d2f420fb-a98a-4327-b8f1-ebb8937f8b00&scope=openid%20profile%20offline_access%20myscope HTTP/1.1
GET /Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fresponse_type%3Dcode%26client_id%3DPowerBI%26redirect_uri%3Dhttps%253A%252F%252Foauth.powerbi.com%252Fviews%252Foauthredirect.html%26state%3Dd2f420fb-a98a-4327-b8f1-ebb8937f8b00%26scope%3Dopenid%2520profile%2520offline_access%2520myscope HTTP/1.1
There's not much else going on. It just simply stops when I click login.

Google Cloud Storage JSON API with JWT Token

I'm trying to use the JSON API for Google Cloud Storage to retrieve a file from Google Cloud Storage. I am not allowed to use the SDKs. Is it possible to create a JWT from a ServiceAccount.json file and use the JWT to access files from Google Cloud Storage? I have a script in node.js that generates a JWT from the service account, but i'm not sure if the audience is right
const jwt = require('jsonwebtoken');
const serviceAccount = require('./serviceAccount.json');
const issuedAt = Math.floor(Date.now() / 1000);
const TOKEN_DURATION_IN_SECONDS = 3600;
let params = {
'iss': serviceAccount.client_email,
'sub': serviceAccount.client_email,
'aud': serviceAccount.project_id,
'iat': issuedAt,
'exp': issuedAt + TOKEN_DURATION_IN_SECONDS,
};
let options = {
algorithm: 'RS256',
header: {
'kid': serviceAccount.private_key_id,
'typ': 'JWT',
'alg': 'RS256',
},
};
let token = jwt.sign(params, serviceAccount.private_key, options);
console.log(token);
I then use that JWT to call the Google Cloud Storage JSON API:
https://www.googleapis.com/storage/v1/b/test
Using the header: Authorization Bearer {token}
That simply resulted in a Invalid Credentials response.
A few questions:
I'm not sure what the 'aud' should be when creating the JWT. I've seen examples where it's a url and also where it's the projectId. Neither work for me.
One of the JSON API examples said the Authorization token should be an oauth token. Can I use a JWT instead or do I need to make a call using the JWT to get an access token?
Is my bucket path correct? Is the base folder for the bucket path your projectId? Should my path be /{projectId}/test. I've tried both and neither work.
Recap
This is an IoT project and I need embedded devices to download files from Google Cloud Storage. I need to create a web portal to upload files to (using Firebase Functions) and pass to the device either a bucket path or a private/signed URL that. The bottom line being I need to access a Google Cloud Storage bucket using a service account key. If there is an embedded SDK - great, but I couldn't find one for C. My only thought was to use the JSON API. If there is a way I can sign a URL which can only be accessed using a service account - that works too.
Thanks!
Yes, you can create your own Signed JWT from a service account Json (or P12) file and exchange the JWT for an Access Token that you then use as Authorization: Bearer TOKEN
I have written a number of articles on how to use Json and P12 credentials.
Google Cloud – Creating OAuth Access Tokens for REST API Calls
For your questions:
I'm not sure what the 'aud' should be when creating the JWT. I've seen
examples where it's a url and also where it's the projectId. Neither
work for me.
Set aud to "https://www.googleapis.com/oauth2/v4/token"
One of the JSON API examples said the Authorization token should be an
oauth token. Can I use a JWT instead or do I need to make a call using
the JWT to get an access token?
Some APIs accept signed JWTs, others expect an OAuth Access Token. It is just easier to always obtain the OAuth Access Token. In my example code below, I show you how.
Is my bucket path correct? Is the base folder for the bucket path your
projectId? Should my path be /{projectId}/test. I've tried both and
neither work.
Your url shold look like this (Python string building example)
url = "https://www.googleapis.com/storage/v1/b?project=" + project
Below I show you how to call two services (GCE and GCS). Most Google APIs will follow similar styles for building the REST API urls.
From the code in your question, you are missing the last step in the OAuth process. You need to exchange your Signed JWT for an Access Token.
def exchangeJwtForAccessToken(signed_jwt):
'''
This function takes a Signed JWT and exchanges it for a Google OAuth Access Token
'''
auth_url = "https://www.googleapis.com/oauth2/v4/token"
params = {
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": signed_jwt
}
r = requests.post(auth_url, data=params)
if r.ok:
return(r.json()['access_token'], '')
return None, r.text
Here is a complete Python 3.x example that will list GCE instances. Below this code are changes to display GCS Buckets.
'''
This program lists lists the Google Compute Engine Instances in one zone
'''
import time
import json
import jwt
import requests
import httplib2
# Project ID for this request.
project = 'development-123456'
# The name of the zone for this request.
zone = 'us-west1-a'
# Service Account Credentials, Json format
json_filename = 'service-account.json'
# Permissions to request for Access Token
scopes = "https://www.googleapis.com/auth/cloud-platform"
# Set how long this token will be valid in seconds
expires_in = 3600 # Expires in 1 hour
def load_json_credentials(filename):
''' Load the Google Service Account Credentials from Json file '''
with open(filename, 'r') as f:
data = f.read()
return json.loads(data)
def load_private_key(json_cred):
''' Return the private key from the json credentials '''
return json_cred['private_key']
def create_signed_jwt(pkey, pkey_id, email, scope):
'''
Create a Signed JWT from a service account Json credentials file
This Signed JWT will later be exchanged for an Access Token
'''
# Google Endpoint for creating OAuth 2.0 Access Tokens from Signed-JWT
auth_url = "https://www.googleapis.com/oauth2/v4/token"
issued = int(time.time())
expires = issued + expires_in # expires_in is in seconds
# Note: this token expires and cannot be refreshed. The token must be recreated
# JWT Headers
additional_headers = {
'kid': pkey_id,
"alg": "RS256",
"typ": "JWT" # Google uses SHA256withRSA
}
# JWT Payload
payload = {
"iss": email, # Issuer claim
"sub": email, # Issuer claim
"aud": auth_url, # Audience claim
"iat": issued, # Issued At claim
"exp": expires, # Expire time
"scope": scope # Permissions
}
# Encode the headers and payload and sign creating a Signed JWT (JWS)
sig = jwt.encode(payload, pkey, algorithm="RS256", headers=additional_headers)
return sig
def exchangeJwtForAccessToken(signed_jwt):
'''
This function takes a Signed JWT and exchanges it for a Google OAuth Access Token
'''
auth_url = "https://www.googleapis.com/oauth2/v4/token"
params = {
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": signed_jwt
}
r = requests.post(auth_url, data=params)
if r.ok:
return(r.json()['access_token'], '')
return None, r.text
def gce_list_instances(accessToken):
'''
This functions lists the Google Compute Engine Instances in one zone
'''
# Endpoint that we will call
url = "https://www.googleapis.com/compute/v1/projects/" + project + "/zones/" + zone + "/instances"
# One of the headers is "Authorization: Bearer $TOKEN"
headers = {
"Host": "www.googleapis.com",
"Authorization": "Bearer " + accessToken,
"Content-Type": "application/json"
}
h = httplib2.Http()
resp, content = h.request(uri=url, method="GET", headers=headers)
status = int(resp.status)
if status < 200 or status >= 300:
print('Error: HTTP Request failed')
return
j = json.loads(content.decode('utf-8').replace('\n', ''))
print('Compute instances in zone', zone)
print('------------------------------------------------------------')
for item in j['items']:
print(item['name'])
if __name__ == '__main__':
cred = load_json_credentials(json_filename)
private_key = load_private_key(cred)
s_jwt = create_signed_jwt(
private_key,
cred['private_key_id'],
cred['client_email'],
scopes)
token, err = exchangeJwtForAccessToken(s_jwt)
if token is None:
print('Error:', err)
exit(1)
gce_list_instances(token)
To display GCS Buckets instead, modify the code:
# Create the HTTP url for the Google Storage REST API
url = "https://www.googleapis.com/storage/v1/b?project=" + project
resp, content = h.request(uri=url, method="GET", headers=headers)
s = content.decode('utf-8').replace('\n', '')
j = json.loads(s)
print('')
print('Buckets')
print('----------------------------------------')
for item in j['items']:
print(item['name'])
I found this [Service account authorization without OAuth].(https://developers.google.com/identity/protocols/oauth2/service-account#jwt-auth
You can avoid having to make a network request to Google's authorization server before making an API call.
Available APIs are listed in https://github.com/googleapis/googleapis.
It looks like Google Cloud Storage api is not yet published as per the comments in the repository.
Were you able to use the cloud storage API with JWT?

How to Short an URL using Google API and REQUESTS?

I am trying to short an URL using Google API but using only the requests module.
The code looks like this:
import requests
Key = "" # found in https://developers.google.com/url-shortener/v1/getting_started#APIKey
api = "https://www.googleapis.com/urlshortener/v1/url"
target = "http://www.google.com/"
def goo_shorten_url(url=target):
payload = {'longUrl': url, "key":Key}
r = requests.post(api, params=payload)
print(r.text)
When I run goo_shorten_url it returns:
"error": {
"errors": [
{
"domain": "global",
"reason": "required",
"message": "Required",
"locationType": "parameter",
"location": "resource.longUrl"
}
],
"code": 400,
"message": "Required"
}
But the longUrl parameter is there!
What am I doing wrong?
At first, please confirm that "urlshortener api v1" is enabled at Google API Console.
Content-Type is required as a header. And please use data as a request parameter. The modified sample is as follows.
Modified sample :
import json
import requests
Key = "" # found in https://developers.google.com/url-shortener/v1/getting_started#APIKey
api = "https://www.googleapis.com/urlshortener/v1/url"
target = "http://www.google.com/"
def goo_shorten_url(url=target):
headers = {"Content-Type": "application/json"}
payload = {'longUrl': url, "key":Key}
r = requests.post(api, headers=headers, data=json.dumps(payload))
print(r.text)
If above script doesn't work, please use an access token. The scope is https://www.googleapis.com/auth/urlshortener. In the case of use of access token, the sample script is as follows.
Sample script :
import json
import requests
headers = {
"Authorization": "Bearer " + "access token",
"Content-Type": "application/json"
}
payload = {"longUrl": "http://www.google.com/"}
r = requests.post(
"https://www.googleapis.com/urlshortener/v1/url",
headers=headers,
data=json.dumps(payload)
)
print(r.text)
Result :
{
"kind": "urlshortener#url",
"id": "https://goo.gl/#####",
"longUrl": "http://www.google.com/"
}
Added 1 :
In the case of use tinyurl.com
import requests
URL = "http://www.google.com/"
r = requests.get("http://tinyurl.com/" + "api-create.php?url=" + URL)
print(r.text)
Added 2 :
How to use Python Quickstart
You can use Python Quickstart. If you don't have "google-api-python-client", please install it. After installed it, please copy paste a sample script from "Step 3: Set up the sample", and create it as a python script. Modification points are following 2 parts.
1. Scope
Before :
SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly'
After :
SCOPES = 'https://www.googleapis.com/auth/urlshortener'
2. Script
Before :
def main():
"""Shows basic usage of the Google Drive API.
Creates a Google Drive API service object and outputs the names and IDs
for up to 10 files.
"""
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('drive', 'v3', http=http)
results = service.files().list(
pageSize=10,fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
else:
print('Files:')
for item in items:
print('{0} ({1})'.format(item['name'], item['id']))
After :
def main():
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('urlshortener', 'v1', http=http)
resp = service.url().insert(body={'longUrl': 'http://www.google.com/'}).execute()
print(resp)
After done the above modifications, please execute the sample script. You can get the short URL.
I am convinced that one CANNOT use ONLY requests to use google api for shorten an url.
Below I wrote the solution I ended up with,
It works, but it uses google api, which is ok, but I cannot find much documentation or examples about it (Not as much as I wanted).
To run the code remember to install google api for python first with
pip install google-api-python-client, then:
import json
from oauth2client.service_account import ServiceAccountCredentials
from apiclient.discovery import build
scopes = ['https://www.googleapis.com/auth/urlshortener']
path_to_json = "PATH_TO_JSON"
#Get the JSON file from Google Api [Website]
(https://console.developers.google.com/apis/credentials), then:
# 1. Click on Create Credentials.
# 2. Select "SERVICE ACCOUNT KEY".
# 3. Create or select a Service Account and
# 4. save the JSON file.
credentials = ServiceAccountCredentials.from_json_keyfile_name(path_to_json, scopes)
short = build("urlshortener", "v1",credentials=credentials)
request = short.url().insert(body={"longUrl":"www.google.com"})
print(request.execute())
I adapted this from Google's Manual Page.
The reason it has to be so complicated (more than I expected at first at least) is to avoid the OAuth2 authentication that requires the user (Me in this case) to press a button (to confirm that I can use my information).
As the question is not very clear this answer is divided in 4 parts.
Shortening URL Using:
1. API Key.
2. Access Token
3. Service Account
4. Simpler solution with TinyUrl.
API Key
At first, please confirm that "urlshortener api v1" is enabled at Google API Console.
Content-Type is required as a header. And please use data as a request parameter. The modified sample is as follows.
(Seems not to be working despite what the API manual says).
Modified sample :
import json
import requests
Key = "" # found in https://developers.google.com/url-shortener/v1/getting_started#APIKey
api = "https://www.googleapis.com/urlshortener/v1/url"
target = "http://www.google.com/"
def goo_shorten_url(url=target):
headers = {"Content-Type": "application/json"}
payload = {'longUrl': url, "key":Key}
r = requests.post(api, headers=headers, data=json.dumps(payload))
print(r.text)
Access Token:
If above script doesn't work, please use an access token. The scope is https://www.googleapis.com/auth/urlshortener. In the case of use of access token, the sample script is as follows.
This answer in Stackoverflow shows how to get an Access Token: Link.
Sample script :
import json
import requests
headers = {
"Authorization": "Bearer " + "access token",
"Content-Type": "application/json"
}
payload = {"longUrl": "http://www.google.com/"}
r = requests.post(
"https://www.googleapis.com/urlshortener/v1/url",
headers=headers,
data=json.dumps(payload)
)
print(r.text)
Result :
{
"kind": "urlshortener#url",
"id": "https://goo.gl/#####",
"longUrl": "http://www.google.com/"
}
Using Service Account
To avoid the user need to accept the OAuth authentication (with a pop up screen and all that) there is a solution that uses authentication from machine to machine using a Service Account (As mentioned in another proposed answer).
To run this part of the code remember to install google api for python first with pip install google-api-python-client, then:
import json
from oauth2client.service_account import ServiceAccountCredentials
from apiclient.discovery import build
scopes = ['https://www.googleapis.com/auth/urlshortener']
path_to_json = "PATH_TO_JSON"
#Get the JSON file from Google Api [Website]
(https://console.developers.google.com/apis/credentials), then:
# 1. Click on Create Credentials.
# 2. Select "SERVICE ACCOUNT KEY".
# 3. Create or select a Service Account and
# 4. save the JSON file.
credentials = ServiceAccountCredentials.from_json_keyfile_name(path_to_json, scopes)
short = build("urlshortener", "v1",credentials=credentials)
request = short.url().insert(body={"longUrl":"www.google.com"})
print(request.execute())
Adapted from Google's Manual Page.
Even simpler:
In the case of use tinyurl.com
import requests
URL = "http://www.google.com/"
r = requests.get("http://tinyurl.com/" + "api-create.php?url=" + URL)
print(r.text)

ASP.NET Identity / OData using CORS and cookie authentication missing Auth cookie

I have an ASP.NET Identity site and a ASP.NET OData site.
Both sites have CORS enabled and both site are using ASP.NET Identity CookieAuthentication.
When I execute both sites locally on my computer using IIS (not express) the AUTH cookie is being passed in the header on each request to the OData site.
But when I deploy the sites to the production IIS server then the header is missing the AUTH cookie when calling the production OData site.
Both production and my local IIS have the same domain name and CORS is setup to allow all.
The WebApiConfig has
cors = new Http.Cors.EnableCorsAttribute("*", "*", "*");
config.Enable(cors);
Before anyone asks, yes the machine key is the same between sites.
UPDATE
This seems to be a CORS issue.
When both sites are on my local machine they use the same host name and domain name but when the site are on the production server they have different host names and the same domain name.
You might need to specify the "Access-Control-Allow-Origin" inside your OAuthAuthorizationServerProvider.
I'm using OWIN but you should be able to do something similarly.
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
try adding the policy in your OWIN startup class as below. Just keep in mind that the Startup class might have some different class files since it's a partial class. Also, check ConfigureAuth method to see if everything is set according to your needs there. For instance, you set the external signin cookie of Identity as copied below in ConfigureAuth method to allow External Signin Cookeies like facebook and google.
public void Configuration(IAppBuilder app)
{
//
app.UseCors(CorsOptions.AllowAll);
ConfigureAuth(app);
}
app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
I finally got this to work.
In the ASP.NET Identity site I have the following:
// configure OAuth for login
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
Provider = new CookieAuthenticationProvider(),
LoginPath = new PathString("/Account/Login.aspx"),
CookieName = ".TESTAUTH",
CookieDomain = ".test.com",
CookieSecure = CookieSecureOption.Always
});
It seems that the important part on the ASP.NET Identity site is that the "CookieName, CookieDomain, and the Machine Key" must match the one on the OData site.
And then on the OData site I have the following:
// configure OAuth for login
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
Provider = new CookieAuthenticationProvider { OnApplyRedirect = ApplyRedirect },
LoginPath = new PathString("/Account/Login.aspx"),
CookieName = ".TESTAUTH",
CookieDomain = ".test.com",
CookieSecure = CookieSecureOption.Always
});
// build the configuration for web api
HttpConfiguration config = new HttpConfiguration();
// Enable CORS (Cross-Origin Resource Sharing) for JavaScript / AJAX calls
// NOTE: USING ALL "*" IS NOT RECOMMENDED
var cors = new Http.Cors.EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
// call the web api startup
WebApiConfig.Register(config);
app.UseWebApi(config);
private void ApplyRedirect(CookieApplyRedirectContext context)
{
Uri absoluteUri = null;
if (Uri.TryCreate(context.RedirectUri, UriKind.Absolute, absoluteUri))
{
var path = PathString.FromUriComponent(absoluteUri);
if (path == context.OwinContext.Request.PathBase + context.Options.LoginPath)
{
QueryString returnURI = new QueryString(context.Options.ReturnUrlParameter, context.Request.Uri.AbsoluteUri);
context.RedirectUri = "https://www.test.com/Account/Login.aspx" + returnURI.ToString;
}
}
context.Response.Redirect(context.RedirectUri);
}
The "LoginPath" is required even though it does not exist on the OData site and you can't use a full url to another site for the login path.
I used "OnApplyRedirect" to redirect to the actual Login page.
I'm not sure what the difference is between "config.EnableCors" and "app.UseCors" but the EnableCors seems to be working for now.