I am making webrequest to instagram api, and recieving 400 bad request, but i guess this is not something specific to instagram and is a generic error in my code.
here is my code."code" paramter in this request is obtained from previous request.Code breaks at "GetResponse" method
string clientID = "myclientid";
string clientsecret="myclientsecret";
string redirectURL = "http://localhost:64341/";
string webrequestUri = "https://api.instagram.com/oauth/
access_token? client_id=" + clientID + "&client_secret=
" + clientsecret + "&grant_type=authorization_code" + "&redirect_uri="redirectURL+"&code="
+Request.QueryString["code"].ToString();
WebRequest request = WebRequest.Create(webrequestUri);
request.Method = "POST";
WebResponse response = request.GetResponse();
C#/asp.net Working example:
namespace InstagramLogin.Code
{
public class InstagramAuth
{
private InstagramClient myApp = new InstagramClient();
public string genUserAutorizationUrl()
{
return String.Format(myApp.OAuthAutorizeURL, myApp.ClientId, myApp.RedirectUri);
}
public AuthUserApiToken getTokenId(string CODE)
{
//curl \-F 'client_id=CLIENT-ID' \
//-F 'client_secret=CLIENT-SECRET' \
//-F 'grant_type=authorization_code' \
//-F 'redirect_uri=YOUR-REDIRECT-URI' \
//-F 'code=CODE' \https://api.instagram.com/oauth/access_token
var wc = new WebClient();
var wcResponse = wc.UploadValues(myApp.AuthAccessTokenUrl,
new System.Collections.Specialized.NameValueCollection() {
{ "client_id", myApp.ClientId },
{ "client_secret", myApp.ClientSecret },
{ "grant_type", "authorization_code"},
{ "redirect_uri", myApp.RedirectUri},
{ "code", CODE}
});
var decodedResponse = wc.Encoding.GetString(wcResponse);
AuthUserApiToken UserApiToken = JsonConvert.DeserializeObject<AuthUserApiToken>(decodedResponse);
return UserApiToken;
}
}
}
your object:
namespace InstagramLogin.Code
{
public class InstagramClient
{
private const string ApiUriDefault = "https://api.instagram.com/v1/";
private const string OAuthUriDefault = "https://api.instagram.com/oauth/";
private const string RealTimeApiDefault = "https://api.instagram.com/v1/subscriptions/";
private const string OAuthAutorizeURLDefault = "https://api.instagram.com/oauth/authorize/?client_id={0}&redirect_uri={1}&response_type=code";
private const string AuthAccessTokenUrlDefault = "https://api.instagram.com/oauth/access_token";
private const string clientId = "clientid";
private const string clientSecretId = "clientsecretid";
private const string redirectUri = "http://localhost/InstagramLogin/InstaAuth.aspx";
private const string websiteUri = "http://localhost/InstagramLogin/InstaAuth.aspx";
public string ApiUri { get; set; }
public string OAuthUri { get; set; }
public string RealTimeApi { get; set; }
public string OAuthAutorizeURL { get { return OAuthAutorizeURLDefault; } }
public string ClientId { get { return clientId; } }
public string ClientSecret { get { return clientSecretId; } }
public string RedirectUri { get { return redirectUri; } }
public string AuthAccessTokenUrl { get { return AuthAccessTokenUrlDefault; } }
//removed props
}
}
instagram loged user:
namespace InstagramLogin.Code
{
public class SessionInstaAuthUser
{
public bool isInSession()
{
return HttpContext.Current.Session["AuthUserApiToken"] != null;
}
public AuthUserApiToken get()
{
if (isInSession())
{
return HttpContext.Current.Session["AuthUserApiToken"] as AuthUserApiToken;
}
return null;
}
public void set(AuthUserApiToken value)
{
HttpContext.Current.Session["AuthUserApiToken"] = value;
}
public void clear()
{
if (isInSession())
{
HttpContext.Current.Session["AuthUserApiToken"] = null;
}
}
}
}
markup:
<asp:Button ID="btnInstaAuth"
Text="Click here to get instagram auth"
runat="server" />
Code behind:
//objects
private InstagramClient InstagramApiCLient = new InstagramClient();
private InstagramAuth AuthManager = new InstagramAuth();
private SessionInstaAuthUser SesssionInstaUser = new SessionInstaAuthUser();
//click login with tests - user is logged (in session)
void btnInstaAuth_Click(Object sender,
EventArgs e)
{
btnGetInstaPosts.Visible = false;
if (SesssionInstaUser.isInSession())
{
SesssionInstaUser.clear();
Button clickedButton = (Button)sender;
clickedButton.Text = "Click here to get instagram auth";
btnInstaAuth.Visible = true;
}
else
{
Response.Redirect(AuthManager.genUserAutorizationUrl());
}
}
You can find what you need in this class InstagramAuth sorry if I did forgot to remove some of extra methods or properties, please strip it out.
This button can be used on all page, still don't forget to add on page load at the page set in instagram as login page, query string parse:
//here you read the token instagram generated and append it to the session, or get the error :)
if (!IsPostBack)
{
if (!SesssionInstaUser.isInSession())
{
if (Request.QueryString["code"] != null)
{
var token = AuthManager.getTokenId(Request.QueryString["code"]);
SesssionInstaUser.set(token);
//set login button to have option to log out
btnInstaAuth.Text = token.user.username + " leave instagtam oAuth";
Response.Redirect(Request.Url.ToString().Split('?')[0]);
}
else
if (Request.QueryString["error"] != null)
{
btnInstaAuth.Text = Request.QueryString["error_description"];
}
}
}
Sorry If I'd missed something, php curl in to c# is implemented in first class.
Update (I did forget something), insta user obj :)
namespace InstagramLogin.Code
{
public class UserLogged
{
public string id { get; set; }
public string username { get; set; }
public string full_name { get; set; }
public string profile_picture { get; set; }
}
public class AuthUserApiToken
{
public string access_token { get; set; }
public UserLogged user { get; set; }
}
}
Related
IMPORTANT - Microsoft is retiring AppCenter Push pretty soon, you can still follow my answer below to implement it. Or you can follow my new post at How to implement Push Notification in Xamarin with Firebase and Apple Push Notification with C# backend on using Firebase & Apple Push Notification. Thank you.
I have been reading https://learn.microsoft.com/en-us/appcenter/push/rest-api and looking over the Internet for example of how to implement this easily but found nothing useful.
I read and implemented this
https://www.andrewhoefling.com/Blog/Post/push-notifications-with-app-center-api-integration. His solution offers very good start, but incomplete.
So I enhanced Andrew Hoefling's solution from above to a full working version and thought it's good to share with fellows Xamarin members in the answer below.
public class AppCenterPush
{
User receiver = new User();
public AppCenterPush(Dictionary<Guid, string> dicInstallIdPlatform)
{
//Simply get all the Install IDs for the receipient with the platform name as the value
foreach(Guid key in dicInstallIdPlatform.Keys)
{
switch(dicInstallIdPlatform[key])
{
case "Android":
receiver.AndroidDevices.Add(key.ToString());
break;
case "iOS":
receiver.IOSDevices.Add(key.ToString());
break;
}
}
}
public class Constants
{
public const string Url = "https://api.appcenter.ms/v0.1/apps";
public const string ApiKeyName = "X-API-Token";
//Push required to use this. Go to https://learn.microsoft.com/en-us/appcenter/api-docs/index for instruction
public const string FullAccessToken = "{FULL ACCESS TOKEN}";
public const string DeviceTarget = "devices_target";
public class Apis { public const string Notification = "push/notifications"; }
//You can find your AppName and Organization/User name at your AppCenter URL as such https://appcenter.ms/users/{owner-name}/apps/{app-name}
public const string AppNameAndroid = "{APPNAME_ANDROID}";
public const string AppNameIOS = "{APPNAME_IOS}";
public const string Organization = "{ORG_OR_USER}";
}
[JsonObject]
public class Push
{
[JsonProperty("notification_target")]
public Target Target { get; set; }
[JsonProperty("notification_content")]
public Content Content { get; set; }
}
[JsonObject]
public class Content
{
public Content()
{
Name = "default"; //By default cannot be empty, must have at least 3 characters
}
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("body")]
public string Body { get; set; }
[JsonProperty("custom_data")]
public IDictionary<string, string> CustomData { get; set; }
}
[JsonObject]
public class Target
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("devices")]
public IEnumerable Devices { get; set; }
}
public class User
{
public User()
{
IOSDevices = new List<string>();
AndroidDevices = new List<string>();
}
public List<string> IOSDevices { get; set; }
public List<string> AndroidDevices { get; set; }
}
public async Task<bool> Notify(string title, string message, Dictionary<string, string> customData = default(Dictionary<string, string>))
{
try
{
//title, message length cannot exceed 100 char
if (title.Length > 100)
title = title.Substring(0, 95) + "...";
if (message.Length > 100)
message = message.Substring(0, 95) + "...";
if (!receiver.IOSDevices.Any() && !receiver.AndroidDevices.Any())
return false; //No devices to send
//To make sure in Android, title and message is retain when click from notification. Else it's lost when app is in background
if (customData == null)
customData = new Dictionary<string, string>();
if (!customData.ContainsKey("Title"))
customData.Add("Title", title);
if (!customData.ContainsKey("Message"))
customData.Add("Message", message);
//custom data cannot exceed 100 char
foreach (string key in customData.Keys)
{
if(customData[key].Length > 100)
{
customData[key] = customData[key].Substring(0, 95) + "...";
}
}
var push = new Push
{
Content = new Content
{
Title = title,
Body = message,
CustomData = customData
},
Target = new Target
{
Type = Constants.DeviceTarget
}
};
HttpClient httpClient = new HttpClient();
//Set the content header to json and inject the token
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add(Constants.ApiKeyName, Constants.FullAccessToken);
//Needed to solve SSL/TLS issue when
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
if (receiver.IOSDevices.Any())
{
push.Target.Devices = receiver.IOSDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameiOS}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
if (receiver.AndroidDevices.Any())
{
push.Target.Devices = receiver.AndroidDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameAndroid}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
return true;
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
}
}
To use this, simply do the following from your program
var receiptInstallID = new Dictionary<string, string>
{
{ "XXX-XXX-XXX-XXX", "Android" },
{ "YYY-YYY-YYY-YYY", "iOS" }
};
AppCenterPush appCenterPush = new AppCenterPush(receiptInstallID);
await appCenterPush.Notify("{YOUR_TITLE}", "{YOUR_MESSAGE}", null);
Can't add comment yet, but theres a typo in the above answer
string URL = $"{Constants.Url}/{Constants.Organization}/Constants.AppNameiOS}/{Constants.Apis.Notification}";
Should be
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameIOS}/{Constants.Apis.Notification}";
Missing { and IOS constant is capitalized.
Also, in your example to call it, Should be constructed as < guid, string >
var receiptInstallID = new Dictionary<Guid, string>
also needed as just FYI:
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
If you want to send notification for target user (user_ids_target),
public class AppCenterPush
{
User receiver = new User();
public AppCenterPush(Dictionary<string, string> dicInstallIdPlatform)
{
//Simply get all the Install IDs for the receipient with the platform name as the value
foreach (string key in dicInstallIdPlatform.Keys)
{
switch (dicInstallIdPlatform[key])
{
case "Android":
receiver.AndroidDevices.Add(key.ToString());
break;
case "iOS":
receiver.IOSDevices.Add(key.ToString());
break;
}
}
}
public class Constants
{
public const string Url = "https://api.appcenter.ms/v0.1/apps";
public const string ApiKeyName = "X-API-Token";
//Push required to use this. Go to https://learn.microsoft.com/en-us/appcenter/api-docs/index for instruction
public const string FullAccessToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
public const string DeviceTarget = "devices_target";
public const string UserTarget = "user_ids_target";
public class Apis { public const string Notification = "push/notifications"; }
//You can find your AppName and Organization/User name at your AppCenter URL as such https://appcenter.ms/users/{owner-name}/apps/{app-name}
public const string AppNameAndroid = "XXXXXX";
public const string AppNameIOS = "XXXXXX";
public const string Organization = "XXXXXXX";
}
[JsonObject]
public class Push
{
[JsonProperty("notification_target")]
public Target Target { get; set; }
[JsonProperty("notification_content")]
public Content Content { get; set; }
}
[JsonObject]
public class Content
{
public Content()
{
Name = "default"; //By default cannot be empty, must have at least 3 characters
}
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("body")]
public string Body { get; set; }
[JsonProperty("custom_data")]
public IDictionary<string, string> CustomData { get; set; }
}
[JsonObject]
public class Target
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("user_ids")]
public IEnumerable Users { get; set; }
}
public class User
{
public User()
{
IOSDevices = new List<string>();
AndroidDevices = new List<string>();
}
public List<string> IOSDevices { get; set; }
public List<string> AndroidDevices { get; set; }
}
public async Task<bool> Notify(string title, string message, Dictionary<string, string> customData = default(Dictionary<string, string>))
{
try
{
//title, message length cannot exceed 100 char
if (title.Length > 100)
title = title.Substring(0, 95) + "...";
if (message.Length > 100)
message = message.Substring(0, 95) + "...";
if (!receiver.IOSDevices.Any() && !receiver.AndroidDevices.Any())
return false; //No devices to send
//To make sure in Android, title and message is retain when click from notification. Else it's lost when app is in background
if (customData == null)
customData = new Dictionary<string, string>();
if (!customData.ContainsKey("Title"))
customData.Add("Title", title);
if (!customData.ContainsKey("Message"))
customData.Add("Message", message);
//custom data cannot exceed 100 char
foreach (string key in customData.Keys)
{
if (customData[key].Length > 100)
{
customData[key] = customData[key].Substring(0, 95) + "...";
}
}
var push = new Push
{
Content = new Content
{
Title = title,
Body = message,
CustomData = customData
},
Target = new Target
{
Type = Constants.UserTarget
}
};
HttpClient httpClient = new HttpClient();
//Set the content header to json and inject the token
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add(Constants.ApiKeyName, Constants.FullAccessToken);
//Needed to solve SSL/TLS issue when
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
if (receiver.IOSDevices.Any())
{
push.Target.Users = receiver.IOSDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameIOS}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
if (receiver.AndroidDevices.Any())
{
push.Target.Users = receiver.AndroidDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameAndroid}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
}
}
After create a function for call it;
public async void PushNotification()
{
var receiptInstallID = new Dictionary<string, string>
{
{"17593989838", "Android" }
};
var customData = new Dictionary<string, string>
{
{"taskId", "1234" }
};
AppCenterPush appCenterPush = new AppCenterPush(receiptInstallID);
await appCenterPush.Notify("Hello", "How are you?", customData);
}
I have found many different solutions with OAuth and either with some libraries or with pure requests (https://github.com/googlesamples/oauth-apps-for-windows).
However, none of the solutions looks like the one that I really need. Currently, my application uses its own database for users to log in using WCF service request (with username and password). However, all users have their domain e-mail created with Google accounts, so I want to add another button "Sign In with Google", which will just make sure that user can also login with his Google e-mail-password pair. I don't need a returned token for the further use etc.
What is the simplest way to achieve this functionality in WPF/C# desktop application?
Here is a self-sufficient, 3rd party-free, WPF sample that does Google auth (it could be easily converted to winforms as well).
If you run it you won't be logged and the app will show you a button. If you click that button, an embedded webbrowser control will run though the Google auth.
Once you are authenticated, the app will just display your name as returned by Google.
Note it's based on official Google's sample here: https://github.com/googlesamples/oauth-apps-for-windows but it uses an embedded browser instead of spawning an external one (and few other differences).
XAML code:
<Window x:Class="GoogleAuth.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="750" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="btv" />
</Window.Resources>
<Grid>
<DockPanel Visibility="{Binding State.IsSigned, Converter={StaticResource btv}}">
<Label>You are signed as:</Label>
<Label Content="{Binding State.Token.Name}" />
</DockPanel>
<Grid Visibility="{Binding State.IsNotSigned, Converter={StaticResource btv}}">
<Grid.RowDefinitions>
<RowDefinition Height="23" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Click="Button_Click">Click to Sign In</Button>
<WebBrowser Grid.Row="1" x:Name="Wb" Height="Auto" />
</Grid>
</Grid>
</Window>
C# code:
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
namespace GoogleAuth
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
State = new OAuthState();
DataContext = this;
}
public OAuthState State { get; }
private void Button_Click(object sender, RoutedEventArgs e)
{
var thread = new Thread(HandleRedirect);
thread.Start();
}
private async void HandleRedirect()
{
State.Token = null;
// for example, let's pretend I want also want to have access to WebAlbums
var scopes = new string[] { "https://picasaweb.google.com/data/" };
var request = OAuthRequest.BuildLoopbackRequest(scopes);
var listener = new HttpListener();
listener.Prefixes.Add(request.RedirectUri);
listener.Start();
// note: add a reference to System.Windows.Presentation and a 'using System.Windows.Threading' for this to compile
await Dispatcher.BeginInvoke(() =>
{
Wb.Navigate(request.AuthorizationRequestUri);
});
// here, we'll wait for redirection from our hosted webbrowser
var context = await listener.GetContextAsync();
// browser has navigated to our small http servern answer anything here
string html = string.Format("<html><body></body></html>");
var buffer = Encoding.UTF8.GetBytes(html);
context.Response.ContentLength64 = buffer.Length;
var stream = context.Response.OutputStream;
var responseTask = stream.WriteAsync(buffer, 0, buffer.Length).ContinueWith((task) =>
{
stream.Close();
listener.Stop();
});
string error = context.Request.QueryString["error"];
if (error != null)
return;
string state = context.Request.QueryString["state"];
if (state != request.State)
return;
string code = context.Request.QueryString["code"];
State.Token = request.ExchangeCodeForAccessToken(code);
}
}
// state model
public class OAuthState : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private OAuthToken _token;
public OAuthToken Token
{
get => _token;
set
{
if (_token == value)
return;
_token = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Token)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSigned)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsNotSigned)));
}
}
public bool IsSigned => Token != null && Token.ExpirationDate > DateTime.Now;
public bool IsNotSigned => !IsSigned;
}
// This is a sample. Fille information (email, etc.) can depend on scopes
[DataContract]
public class OAuthToken
{
[DataMember(Name = "access_token")]
public string AccessToken { get; set; }
[DataMember(Name = "token_type")]
public string TokenType { get; set; }
[DataMember(Name = "expires_in")]
public int ExpiresIn { get; set; }
[DataMember(Name = "refresh_token")]
public string RefreshToken { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Email { get; set; }
[DataMember]
public string Picture { get; set; }
[DataMember]
public string Locale { get; set; }
[DataMember]
public string FamilyName { get; set; }
[DataMember]
public string GivenName { get; set; }
[DataMember]
public string Id { get; set; }
[DataMember]
public string Profile { get; set; }
[DataMember]
public string[] Scopes { get; set; }
// not from google's response, but we store this
public DateTime ExpirationDate { get; set; }
}
// largely inspired from
// https://github.com/googlesamples/oauth-apps-for-windows
public sealed class OAuthRequest
{
// TODO: this is a sample, please change these two, use your own!
private const string ClientId = "581786658708-elflankerquo1a6vsckabbhn25hclla0.apps.googleusercontent.com";
private const string ClientSecret = "3f6NggMbPtrmIBpgx-MK2xXK";
private const string AuthorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
private const string TokenEndpoint = "https://www.googleapis.com/oauth2/v4/token";
private const string UserInfoEndpoint = "https://www.googleapis.com/oauth2/v3/userinfo";
private OAuthRequest()
{
}
public string AuthorizationRequestUri { get; private set; }
public string State { get; private set; }
public string RedirectUri { get; private set; }
public string CodeVerifier { get; private set; }
public string[] Scopes { get; private set; }
// https://developers.google.com/identity/protocols/OAuth2InstalledApp
public static OAuthRequest BuildLoopbackRequest(params string[] scopes)
{
var request = new OAuthRequest
{
CodeVerifier = RandomDataBase64Url(32),
Scopes = scopes
};
string codeChallenge = Base64UrlEncodeNoPadding(Sha256(request.CodeVerifier));
const string codeChallengeMethod = "S256";
string scope = BuildScopes(scopes);
request.RedirectUri = string.Format("http://{0}:{1}/", IPAddress.Loopback, GetRandomUnusedPort());
request.State = RandomDataBase64Url(32);
request.AuthorizationRequestUri = string.Format("{0}?response_type=code&scope=openid%20profile{6}&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
AuthorizationEndpoint,
Uri.EscapeDataString(request.RedirectUri),
ClientId,
request.State,
codeChallenge,
codeChallengeMethod,
scope);
return request;
}
// https://developers.google.com/identity/protocols/OAuth2InstalledApp Step 5: Exchange authorization code for refresh and access tokens
public OAuthToken ExchangeCodeForAccessToken(string code)
{
if (code == null)
throw new ArgumentNullException(nameof(code));
string tokenRequestBody = string.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&client_secret={4}&scope=&grant_type=authorization_code",
code,
Uri.EscapeDataString(RedirectUri),
ClientId,
CodeVerifier,
ClientSecret
);
return TokenRequest(tokenRequestBody, Scopes);
}
// this is not used in this sample, but can be used to refresh a token from an old one
// https://developers.google.com/identity/protocols/OAuth2InstalledApp Refreshing an access token
public OAuthToken Refresh(OAuthToken oldToken)
{
if (oldToken == null)
throw new ArgumentNullException(nameof(oldToken));
string tokenRequestBody = string.Format("refresh_token={0}&client_id={1}&client_secret={2}&grant_type=refresh_token",
oldToken.RefreshToken,
ClientId,
ClientSecret
);
return TokenRequest(tokenRequestBody, oldToken.Scopes);
}
private static T Deserialize<T>(string json)
{
if (string.IsNullOrWhiteSpace(json))
return default(T);
return Deserialize<T>(Encoding.UTF8.GetBytes(json));
}
private static T Deserialize<T>(byte[] json)
{
if (json == null || json.Length == 0)
return default(T);
using (var ms = new MemoryStream(json))
{
return Deserialize<T>(ms);
}
}
private static T Deserialize<T>(Stream json)
{
if (json == null)
return default(T);
var ser = CreateSerializer(typeof(T));
return (T)ser.ReadObject(json);
}
private static DataContractJsonSerializer CreateSerializer(Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
var settings = new DataContractJsonSerializerSettings
{
DateTimeFormat = new DateTimeFormat("yyyy-MM-dd'T'HH:mm:ss.fffK")
};
return new DataContractJsonSerializer(type, settings);
}
// https://stackoverflow.com/questions/223063/how-can-i-create-an-httplistener-class-on-a-random-port-in-c/
private static int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
private static string RandomDataBase64Url(int length)
{
using (var rng = new RNGCryptoServiceProvider())
{
var bytes = new byte[length];
rng.GetBytes(bytes);
return Base64UrlEncodeNoPadding(bytes);
}
}
private static byte[] Sha256(string text)
{
using (var sha256 = new SHA256Managed())
{
return sha256.ComputeHash(Encoding.ASCII.GetBytes(text));
}
}
private static string Base64UrlEncodeNoPadding(byte[] buffer)
{
string b64 = Convert.ToBase64String(buffer);
// converts base64 to base64url.
b64 = b64.Replace('+', '-');
b64 = b64.Replace('/', '_');
// strips padding.
b64 = b64.Replace("=", "");
return b64;
}
private static OAuthToken TokenRequest(string tokenRequestBody, string[] scopes)
{
var request = (HttpWebRequest)WebRequest.Create(TokenEndpoint);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
byte[] bytes = Encoding.ASCII.GetBytes(tokenRequestBody);
using (var requestStream = request.GetRequestStream())
{
requestStream.Write(bytes, 0, bytes.Length);
}
var response = request.GetResponse();
using (var responseStream = response.GetResponseStream())
{
var token = Deserialize<OAuthToken>(responseStream);
token.ExpirationDate = DateTime.Now + new TimeSpan(0, 0, token.ExpiresIn);
var user = GetUserInfo(token.AccessToken);
token.Name = user.Name;
token.Picture = user.Picture;
token.Email = user.Email;
token.Locale = user.Locale;
token.FamilyName = user.FamilyName;
token.GivenName = user.GivenName;
token.Id = user.Id;
token.Profile = user.Profile;
token.Scopes = scopes;
return token;
}
}
private static UserInfo GetUserInfo(string accessToken)
{
var request = (HttpWebRequest)WebRequest.Create(UserInfoEndpoint);
request.Method = "GET";
request.Headers.Add(string.Format("Authorization: Bearer {0}", accessToken));
var response = request.GetResponse();
using (var stream = response.GetResponseStream())
{
return Deserialize<UserInfo>(stream);
}
}
private static string BuildScopes(string[] scopes)
{
string scope = null;
if (scopes != null)
{
foreach (var sc in scopes)
{
scope += "%20" + Uri.EscapeDataString(sc);
}
}
return scope;
}
// https://developers.google.com/+/web/api/rest/openidconnect/getOpenIdConnect
[DataContract]
private class UserInfo
{
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "kind")]
public string Kind { get; set; }
[DataMember(Name = "email")]
public string Email { get; set; }
[DataMember(Name = "picture")]
public string Picture { get; set; }
[DataMember(Name = "locale")]
public string Locale { get; set; }
[DataMember(Name = "family_name")]
public string FamilyName { get; set; }
[DataMember(Name = "given_name")]
public string GivenName { get; set; }
[DataMember(Name = "sub")]
public string Id { get; set; }
[DataMember(Name = "profile")]
public string Profile { get; set; }
[DataMember(Name = "gender")]
public string Gender { get; set; }
}
}
}
This code uses an embedded Internet Explorer control, but since Google doesn't support old Internet Explorer versions, it's likely that you also need to add some code to play with IE's compatibility features as described here for example: https://stackoverflow.com/a/28626667/403671
You can put that code in App.xaml.cs, like this:
public partial class App : Application
{
public App()
{
// use code from here: https://stackoverflow.com/a/28626667/403671
SetWebBrowserFeatures();
}
}
Note what the webbrowser will display depends entirely on Google, and it can considerably vary depending on the environment like cookies, language, etc.
I think you are missing the point on what the OAuth is, start by reading this and here is a simple description of the flow.
which will just make sure that user can also login with his Google e-mail-password pair, I don't need a returned token for the further use
There is no api for user/pass validation, and the token is the whole point of the OAuth. A user with existing email should be able to login if indeed you registered your application with google. Also i see no point in prior validation of user/pass, just add the button.
I have created a power bi report. I want to embed this report to my MVC site. Here is my code:-
private static readonly string ClientID = ConfigurationManager.AppSettings["ClientID"];
private static readonly string ClientSecret = ConfigurationManager.AppSettings["ClientSecret"];
private static readonly string RedirectUrl = ConfigurationManager.AppSettings["RedirectUrl"];
private static readonly string AADAuthorityUri = ConfigurationManager.AppSettings["AADAuthorityUri"];
private static readonly string PowerBiAPI = ConfigurationManager.AppSettings["PowerBiAPI"];
private static readonly string PowerBiDataset = ConfigurationManager.AppSettings["PowerBiDataset"];
private static readonly string baseUri = PowerBiDataset;
private static string accessToken = string.Empty;
public class PBIReports
{
public PBIReport[] value { get; set; }
}
public class PBIReport
{
public string id { get; set; }
public string name { get; set; }
public string webUrl { get; set; }
public string embedUrl { get; set; }
}
public ReportController()
{
try
{
if (Request.Params.Get("code") != null)
{
Session["AccessToken"] = GetAccessToken(
HttpContext.Request["code"],
ClientID,
ClientSecret,
RedirectUrl);
}
if (Session["AccessToken"] != null)
{
accessToken = Session["AccessToken"].ToString();
GetReport(0);
}
}
catch(Exception ex)
{
}
}
public string GetAccessToken(string authorizationCode, string clientID, string clientSecret, string redirectUri)
{
TokenCache TC = new TokenCache();
string authority = AADAuthorityUri;
AuthenticationContext AC = new AuthenticationContext(authority, TC);
ClientCredential cc = new ClientCredential(clientID, clientSecret);
return AC.AcquireTokenByAuthorizationCodeAsync(
authorizationCode,
new Uri(redirectUri), cc).Result.AccessToken;
}
protected void GetReport(int index)
{
System.Net.WebRequest request = System.Net.WebRequest.Create(
String.Format("{0}/Reports",
baseUri)) as System.Net.HttpWebRequest;
request.Method = "GET";
request.ContentLength = 0;
request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken));
using (var response = request.GetResponse() as System.Net.HttpWebResponse)
{
using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
{
PBIReports Reports = JsonConvert.DeserializeObject<PBIReports>(reader.ReadToEnd());
if (Reports.value.Length > 0)
{
var report = Reports.value[index];
ViewBag["ReportId"] = report.id;
ViewBag["EmbedUrl"] = report.embedUrl;
ViewBag["ReportName"] = report.name;
ViewBag["WebUrl"] = report.webUrl;
}
}
}
}
public void GetAuthorizationCode()
{
var #params = new NameValueCollection
{
{"response_type", "code"},
{"client_id", ClientID},
{"resource", PowerBiAPI},
{ "redirect_uri", RedirectUrl}
};
var queryString = HttpUtility.ParseQueryString(string.Empty);
queryString.Add(#params);
Response.Redirect(String.Format(AADAuthorityUri + "?{0}", queryString));
}
public ActionResult Index()
{
GetAuthorizationCode();
return View();
}
On Redirecting to "Report Page", it goes for power bi login page and after I sign in it redirects back to this page (as homepage and redirect url are same). But Request.Params.Get("code") != null is null. HttpContext.Request is also null. Why? Is there anything wrong in the code?
In the constructor of a controller you don't have the this.HttpContext object to your disposal yet. You might have HttpContext.Current, but I am not sure of that.
What you should do is move that code to an action (for example your GetReport action), where you can make your checks.
I'm trying to load a grid with json that I receive. This is my code in Home.cs:
private void getTickets()
{
try
{
string urlName = "tickets";
string method = "GET";
string json = null;
HttpStatusCode statusCode = HttpStatusCode.Forbidden;
Tickets data = null;
RestClient client = RequestClient.makeClient();
MakeRequest request = new MakeRequest(null, null, urlName, method);
IRestResponse response = client.Execute(request.exec());
statusCode = response.StatusCode;
json = response.Content;
var ticketWrapper = JsonConvert.DeserializeObject<TicketWrapper>(json);
if ((int)statusCode == 200)
{
gridTicket.DataSource = ticketWrapper.tickets;
}
else
{
MessageBox.Show("error");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Ticket wrapper class
class TicketWrapper
{
public IEnumerable<Tickets> tickets { get; set; }
}
Tickets class
class Tickets
{
public int id;
public string title;
public string description;
public int user_id;
public int subject_id;
}
If I debug I can see that I receive the json but ticketWrapper is null what could be wrong here?
Debug image:
Try to change public fields in Ticket class to properties:
class Tickets
{
public int id { get; set; }
public string title { get; set; }
public string description { get; set; }
public int user_id { get; set; }
public int subject_id { get; set; }
}
Also I think that IEnumerable is not the best option for serialization. Try to use List in TicketWrapper.
Additionally move your break point down, because in current position ticketWrapper will be always null (expression has not yet been executed).
I found some new APIs in Windows 10 Mobile device portal that allows to run applications on user phone .
you can launch this like to see the result : http://{PhoneIP}/api/app/packagemanager/packages
and there's another API to launch applications :
api/taskmanager/app
Starts a modern app
HTTP verb: POST
Parameters
appid : PRAID of app to start, hex64 encoded
package : Full name of the app package, hex64 encoded
I have this code to run applications but doesn't work any idea ?
public class PackageInfoToRun
{
public string appid { get; set; }
public string package { get; set; }
public string PackageFamilyName { get; set; }
}
public class PhoneInstalledPackages
{
public Installedpackage[] InstalledPackages { get; set; }
}
public class Installedpackage
{
public string Name { get; set; }
public string PackageFamilyName { get; set; }
public string PackageFullName { get; set; }
public int PackageOrigin { get; set; }
public string PackageRelativeId { get; set; }
public bool IsXAP { get; set; }
}
private static string Encode(string strn)
{
var toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(strn);
string appName64 = System.Convert.ToBase64String(toEncodeAsBytes);
appName64 = appName64.Replace(" ", "20%");
return appName64;
}
public async Task<PhoneInstalledPackages> GetInstalledApps()
{
// /api/app/packagemanager/packages
HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create("http://127.0.0.1/api/app/packagemanager/packages");
string res = "";
webrequest.Method = "GET";
try
{
using (var webresponse = await webrequest.GetResponseAsync())
using (StreamReader loResponseStream = new StreamReader(webresponse.GetResponseStream()))
{
res = loResponseStream.ReadToEnd();
}
var des = JsonConvert.DeserializeObject<PhoneInstalledPackages>(res);
return des;
}
catch (Exception ex)
{
return null;
}
}
public async Task<bool> RunAppAsync(string appid, string packagename)
{
HttpResponseMessage http = new HttpResponseMessage();
string str;
try
{
var package = new PackageInfoToRun()
{
appid = Encode(appid),
package = Encode(packagename)
};
using (HttpClient client = new HttpClient())
{
var serial = JsonConvert.SerializeObject(package);
http = await client.PostAsync("http://127.0.0.1/api/taskmanager/app", new StringContent(serial, Encoding.UTF8, "application/json"));
using (HttpResponseMessage response = http)
{
str = await response.Content.ReadAsStringAsync();
string retur = str;
if (retur.Contains("true"))
{
return true;
}
return false;
}
}
}
catch (Exception ex)
{
return false;
}
}
what's going wrong in my codes ? thanks :)
I used your code with small changes and it works for me.
now RunAppAsync looks like this. Don't forget to specify a PORT.
HttpClient client = new HttpClient();
var serial = JsonConvert.SerializeObject(package);
var result = await client.PostAsync(String.Format("http://127.0.0.1:*PORT*/api/taskmanager/app?appid={0}&package={1}", package.appid, package.package), new StringContent(serial));
Where the package is
var package = new PackageInfoToRun()
{
appid = Encode(app.PackageRelativeId),
package = Encode(app.PackageFullName),
};
And the app variable is an instance of InstalledPackage class.