Rest API Authentication Issue..
Hi there. I'm using the Rest API & trying to authenticate a user. using C#. I'm getting the following error:
StatusCode: 400, ReasonPhrase: 'Bad Request'
I'm following the documentation : (I cannot insert the link due to my account being new) however it's entitled
Getting an access token
2.1. Getting a token for a User Based Server Application (Fig. 1).
I will omit the variable content due to privacy..
The clientId & clientSecret are exactly as was displayed in the API Clients Page & they are of the type "User Based Server Application".
Just want to confirm that I'm using the correct parameters & url paths where they expected.
var userPassword = clientId.ToLower() + ":" + clientSecret.ToLower();
userPassword = Convert.ToBase64String(Encoding.ASCII.GetBytes(userPassword));
var appKey = ***application key under System -> Identity Providers ***
var userKey = *** lowercase username ***
var password = Convert.ToBase64String(sha256(userKey.ToLower() + "|" + appKey.ToLower()));
HttpClientHandler handler = new HttpClientHandler();
HttpClient client = new HttpClient(handler);
client.BaseAddress = new Uri(baseUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", userPassword);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "/Panopto/oauth2/connect/token");
request.Headers.Add("Grant_type", "password");
request.Headers.Add("Username", userKey.ToLower());
request.Headers.Add("Password", password);
request.Headers.Add("scope", "api");
Thanks in advance!
Answers
@paul redmond I responded to a similar question: https://community.panopto.com/discussion/1180/retrieve-folder-id-by-searching-on-folder-name
Your case looks like the Username isn't scoped instancename\USERNAME. You might also have the "sha256 is not used for internal users" issue too, I can't tell for sure. I hope this helps! 😁
Thanks for your reply Jonathan. I've tried multiple times & am still getting the "Bad Response", 400 error.
I added the instance name ie. panoptointernal\USERNAME & removed the sha256.
Considering this is an Api Client -> User Based Server Application I'm assuming the USERNAME is the "Client Name" within the Api Client settings?
Just to confirm when you mention PASSWORD, I've tried using the client secret here & also tried populating it with the Base64String (clientId.ToLower() + ":" + clientSecret.ToLower()).
Hi Paul,
In looking at the sample you provided, it looks like you are adding the grant_type, username, password, and scope to the headers of the request. Is that correct?
If so, those parameters should instead be moved to be form parameters on the request, and not in the headers. That would explain why you are getting an HTTP 400 response.
On the user name, is the user an internal Panopto user, or is the user coming from an external ID Provider? If they are internal, then you shouldn't need to add the application key, or hash the password using SHA-256. For internal users, you should just need to send their user key (without the instance name) and password in their respective fields for the POST request.
I hope this helps, please let me know if you have any other questions.
Thanks,
Kevin
Hi Kevin. Thanks for your response. I was trying a user with SSO however I have since created an internal user for simplicity sake..
This is what I'm currently working with & still getting the 400 bad request.
userPassword = clientId.ToLower() + ":" + clientSecret.ToLower();
userPassword = Convert.ToBase64String(Encoding.ASCII.GetBytes(userPassword));
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(baseUrl);
var req = new HttpRequestMessage(HttpMethod.Post, "/Panopto/oauth2/connect/token");
req.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
req.Headers.Authorization = new AuthenticationHeaderValue("Basic", userPassword);
req.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{"Grant_type", "password" },
{"Username", internalUserKey },
// -> INSTANCENAME\\USERNAME which looks like this -> panoptointernal\\USERNAME
// also tried panoptointernal\USERNAME with a single slash
{"Password", internalUserPassword }, // -> internal Users password
{"Scope", "api" }
});
Just an FYI so nobody wastes time looking at this I resolved it earlier today and I'll update tomorrow with findings after I test a couple of things. Cheers
The issue I was having was I was lowercasing my clientId & secret. What I did find out though was that instead of passing the authorization header "Basic " + auth_key is that you can just add the parameters "client_id" & "client_secret" in the body of the request with the others & that will validate also.
Another thing is the USERNAME does not need the instancename\USERNAME it can just be USERNAME.
Thanks for all who chimed in..
Hi @paul redmond
Would you be able to share the working code here in case anyone else runs into a similar issue?
Sure thing Jonathon.
authKey = clientId + ":" + clientSecret;
authKey = Convert.ToBase64String(Encoding.ASCII.GetBytes(userPassword));
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(baseUrl);
HttpRequestMessage tokenRequest = new HttpRequestMessage(HttpMethod.Post, "/Panopto/oauth2/connect/token");
tokenRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
tokenRequest.Headers.Authorization = new AuthenticationHeaderValue("Basic", authKey );
HttpContent httpContent = new FormUrlEncodedContent(
new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", internalUserKey),
new KeyValuePair<string, string>("password", internalUserPassword),
new KeyValuePair<string, string>("scope", "api")
});
tokenRequest.Content = httpContent;
tokenRequest.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
var response = Task.Run(async () => await client.SendAsync(tokenRequest));
Task<string> values = response.Result.Content.ReadAsStringAsync();
var retToken = values.Result;
Sure thing Jonathon.
authKey = clientId + ":" + clientSecret;
authKey = Convert.ToBase64String(Encoding.ASCII.GetBytes(authKey));
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(baseUrl);
HttpRequestMessage tokenRequest = new HttpRequestMessage(HttpMethod.Post, "/Panopto/oauth2/connect/token");
tokenRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
tokenRequest.Headers.Authorization = new AuthenticationHeaderValue("Basic", authKey );
HttpContent httpContent = new FormUrlEncodedContent(
new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", internalUserKey),
new KeyValuePair<string, string>("password", internalUserPassword),
new KeyValuePair<string, string>("scope", "api")
});
tokenRequest.Content = httpContent;
tokenRequest.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
var response = Task.Run(async () => await client.SendAsync(tokenRequest));
Task<string> values = response.Result.Content.ReadAsStringAsync();
var retToken = values.Result;
That all makes sense to me and looks like what I would expect. They might be a small typo on the second line? I would expect it to be
authKey
that gets converted to base64 instead of the previously undefineduserPassword
. Something like:you're correct.. I changed the variable name as I was pasting it to make more sense to the reader. -- will update it.
Thanks