Sign a hash with CoSign SOAP API - cosign-api

I have an sha256 hash and my goal is to sign it with CoSign SOAP API. Could you provide me a Java example? The PDF Signing example is working like a charm, but it's not perfectly clear for me, how should I set the InputDocument for this task.
Thanks a lot!

The code example below demonstrates how to sign a hash value using CoSign Signature SOAP API. However, I would strongly recommend to use CoSign Signature API instead, which is richer in its functionality and is way more intuitive.
public static byte[] SAPIWS_SignHash(byte[] data, String username, String domain, String password) throws Exception
{
byte[] signedHash = null;
try {
// Set hash method & data
DocumentHash documentHash = new DocumentHash();
DigestMethodType digestMethod = new DigestMethodType();
digestMethod.setAlgorithm("http://www.w3.org/2001/04/xmlenc#sha256");
documentHash.setDigestMethod(digestMethod);
documentHash.setDigestValue(data);
// Set user credentials
ClaimedIdentity claimedIdentity = new ClaimedIdentity();
NameIdentifierType nameIdentifier = new NameIdentifierType();
nameIdentifier.setValue(username);
nameIdentifier.setNameQualifier(domain);
CoSignAuthDataType coSignAuthData = new CoSignAuthDataType();
coSignAuthData.setLogonPassword(password);
claimedIdentity.setName(nameIdentifier);
claimedIdentity.setSupportingInfo(coSignAuthData);
// Build complete request object
SignRequest signRequest = new SignRequest();
RequestBaseType.InputDocuments inputDocs = new RequestBaseType.InputDocuments();
inputDocs.getOtherOrTransformedDataOrDocument().add(documentHash);
RequestBaseType.OptionalInputs optionalParams = new RequestBaseType.OptionalInputs();
optionalParams.setSignatureType("urn:ietf:rfc:2437:RSASSA-PKCS1-v1_5");
optionalParams.setClaimedIdentity(claimedIdentity);
signRequest.setOptionalInputs(optionalParams);
signRequest.setInputDocuments(inputDocs);
// Initiate service client
DSS client = new DSS(new URL("https://prime.cosigntrial.com:8080/sapiws/dss.asmx"));
// Send the request
DssSignResult response = client.getDSSSoap().dssSign(signRequest);
// Check response output
if ("urn:oasis:names:tc:dss:1.0:resultmajor:Success".equals(response.getResult().getResultMajor())) {
// On success- return signature buffer
ResponseBaseType.OptionalOutputs doc = response.getOptionalOutputs();
signedHash = doc.getDocumentWithSignature().getDocument().getBase64Data().getValue();
}
else {
throw new Exception(response.getResult().getResultMessage().getValue());
}
}
catch (Exception e)
{
System.out.println("Error: " + e.getMessage());
e.printStackTrace();
}
return signedHash;
}

Related

FCM Authorization always fails

Today i wanted to switch from GCM to FCM so i set up everything needed and wanted to implement the server side code. I used the gcm4j library and changed it so that the adress goes to https://fcm.googleapis.com/fcm/send.
So im doing the following:
FCM fcm = new FCMDefault(new FCMConfig().withKey(FCMGlobals.FCM_API_KEY));
FCMRequest request = new FCMRequest().withRegistrationId(android.getRegistration())
// .withCollapseKey(collapseKey)
.withDelayWhileIdle(true)
.withDataItem(FCMGlobals.FCM_PARAM_CODE, code)
.withDataItem(FCMGlobals.FCM_PARAM_USER_ID, "" + user.getId())
.withDataItem(FCMGlobals.FCM_PARAM_ADDITION, "" + addition);
ListenableFuture<FCMResponse> responseFuture = fcm.send(request);
Futures.addCallback(responseFuture, new FutureCallback<FCMResponse>() {
public void onFailure(Throwable t) {
log.error(t);
}
#Override
public void onSuccess(FCMResponse response) {
log.info(response.toString());
}
});
The implementation for that is:
protected FCMResponse executeRequest(FCMRequest request) throws IOException {
byte[] content = this.objectMapper.writeValueAsBytes(request);
HttpURLConnection conn = this.connectionFactory.open(this.fcmUrl);
conn.setRequestMethod("POST");
conn.addRequestProperty("Authorization", getAuthorization(request));
conn.addRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
conn.setFixedLengthStreamingMode(content.length);
LoggerFactory.getLogger("FCMDefaultAbstract").info("Authorization: " + conn.getRequestProperty("Authorization"));
LoggerFactory.getLogger("FCMDefaultAbstract").info("Content-Type: " + conn.getRequestProperty("Content-Type"));
LoggerFactory.getLogger("FCMDefaultAbstract").info("send: " + new String(content));
try (OutputStream outputStream = conn.getOutputStream()) {
IOUtils.write(content, outputStream);
} catch (Exception e) {
throw new FCMNetworkException("Error sending HTTP request to FCM", e);
}
FCMResponse response;
try (InputStream inputStream = conn.getInputStream()) {
response = this.objectMapper.readValue(IOUtils.toByteArray(inputStream), FCMResponse.class);
} catch (IOException e) {
try (InputStream inputStreamError = conn.getErrorStream()) {
String str = inputStreamError != null ? IOUtils.toString(inputStreamError) : "No error details provided";
int responseCode = conn.getResponseCode();
if (responseCode < 500) {
throw new FCMNetworkException(conn.getResponseCode(), str.trim(), e);
} else {
throw new FCMNetworkException(conn.getResponseCode(), str.trim(), checkForRetryInResponse(conn), e);
}
}
}
response.setRequest(request);
response.setRetryAfter(checkForRetryInResponse(conn));
Iterator<String> iteratorId = request.getRegistrationIds().iterator();
Iterator<FCMResult> iteratorResponse = response.getResults().iterator();
while (iteratorId.hasNext() && iteratorResponse.hasNext()) {
iteratorResponse.next().setRequestedRegistrationId(iteratorId.next());
}
if (iteratorId.hasNext()) {
LOG.warn("Protocol error: Less results than requested registation IDs");
}
if (iteratorResponse.hasNext()) {
LOG.warn("Protocol error: More results than requested registation IDs");
}
return response;
}
Here the log output:
FCMDefaultAbstract Authorization: null
FCMDefaultAbstract Content-Type:application/json
FCMDefaultAbstract send: {"registration_ids":["dMpvzp*************************************2lRsSl_5lFET2"],"data":{"CODE":"201","USER_ID":"1","ADDITION":"1468083549493"},"delay_while_idle":true}
FCM FCMNetworkException: HTTP 401: No error details provided
The Authorization header is not null in fact. it is correctly set with my FCM API Key. Only the HTTPUrlConnection implementation says to return null if someone trys to access Authorization key.
As you can see i am not able to connect with FCM. The Code 401 means that authentication failed.
What could be the problem here?
Check that you are using a server type API-KEY, and not a client or browser API-KEY.
If you are using Firebase you can find the API-KEY in
Project Settings > Cloud Messaging
If you are using cloud console, or you are not sure which key you are using,
you can generate a new key through through https://console.cloud.google.com
Quoting the documentation
https://firebase.google.com/docs/cloud-messaging/concept-options#credentials
Server key: A server key that authorizes your app server for access to
Google services, including sending messages via Firebase Cloud
Messaging. [...]
Important: Do not include the server key anywhere in your client code.
Also, make sure to use only server keys to authorize your app server.
Android, iOS, and browser keys are rejected by FCM.

Xamarin Android - Can a WCF SOAP webservice use HttpClient for TLS 1.2?

I have a customer who requires TLS 1.2 for PCI compliance. Xamarin Android does not support TLS 1.2 very well. According to this
Native HttpClientHandler and this Transport Layer Security, you can either use HttpClient with their special mechanism to access the native Java support on Android 5 and higher, or you can use the ModernHttpClient.
However, WCF SOAP Webservice proxies generated with SvcUtil appear to use HttpWebRequest, and not HttpClient.
What's the recommended way to call WCF SOAP services using HttpClient (or ModernHttpClient)? Will I have to manually write my own interfaces or can I use the proxy classes and serialize/deserialize them myself? I'd rather not have to completely start from scratch, especially since it looks like TLS 1.2 is currently being added to Mono.
i have used that type of service and it is working here i have share relevant code please try it.
static void TryByWebRequest(string soapMethod)
{
XmlDocument soapEnvelopeXml = new XmlDocument();
soapEnvelopeXml.LoadXml(#"
<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
<s:Body>
<" + soapMethod + #"
xmlns=""your URI""
xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
<InputXMLString>
" +
System.Security.SecurityElement.Escape(inputXML)
+ #"
</InputXMLString>
<OutputXMLString/>
</" + soapMethod + #">
</s:Body>
</s:Envelope>");
using (Stream stream = request.GetRequestStream())
{
soapEnvelopeXml.Save(stream);
}
using (WebResponse response = request.GetResponse())
{
using (StreamReader rd = new StreamReader(response.GetResponseStream()))
{
string soapResult = rd.ReadToEnd();
Console.WriteLine(soapResult);
}
}
}
static HttpWebRequest CreateWebRequest(string soapMethod)
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(#"Your .asmx URL ");
webRequest.Headers.Add(#"SOAPAction", "your URI \" + soapMethod);
webRequest.ContentType = "text/xml;charset=\"utf-8\"";
webRequest.Accept = "text/xml";
webRequest.Method = "POST";
return webRequest;
}
I got this working. Since this is a (hopefully) temporary solution, my goal was to create drop-in replacements for the Proxy-generated classes, and I got pretty close. The key was to figure out how to use the DataContractSerializer to create a SOAP envelope to send, and deserialize the results.
I was successful with everything except for the serialization of the SOAP envelope that gets sent to the web service. I ended up manually wrapping the XML in the <envelope> and <body> tags. Nothing that I did could get the DataContractSerializer to create these correctly, although the Body contents were ok. The Deserializer was able to handle the response from the web service without any problem though. WCF services are very picky about the format of the SOAP envelope, and getting the classes annotated just right was a challenge.
For each function call, I had to create a Request object that wraps the parameters being sent to the web service, and a Response object that wraps the out parameters and the return code.
These look something like this, where the FunctionName is the name of the WCF function call that the proxys generated.
// request envelope
[System.Runtime.Serialization.DataContractAttribute(Name = "FunctionName", Namespace = "http://tempuri.org/")]
public class FunctionName_Request
{
[System.Runtime.Serialization.DataMemberAttribute()]
public NameSpaceFunctionNameObject1 CallingObject1;
[System.Runtime.Serialization.DataMemberAttribute()]
public NameSpaceFunctionNameObject2 CallingObject2;
}
// response envelope
[System.Runtime.Serialization.DataContractAttribute(Name = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class FunctionName_ResponseEnvelope
{
[System.Runtime.Serialization.DataContractAttribute(Name = "Body", Namespace = "http://tempuri.org/")]
public class FunctionName_ResponseBody
{
[System.Runtime.Serialization.DataContractAttribute(Name = "FunctionNameResponse", Namespace = "http://tempuri.org/")]
public class FunctionName_Response
{
[System.Runtime.Serialization.DataMemberAttribute()]
public FunctionNameReturnCodes Result;
[System.Runtime.Serialization.DataMemberAttribute()]
public FunctionNameResponseObject Response;
}
[System.Runtime.Serialization.DataMemberAttribute()]
public FunctionName_Response FunctionNameResponse;
}
[System.Runtime.Serialization.DataMemberAttribute()]
public FunctionName_ResponseBody Body;
}
Then, I can write a replacement function that my client code can call, which has exactly the same signature as the original Proxy-generated function.
// FunctionName
public FunctionNameReturnCodes FunctionName(out FunctionNameResponseObject Response, NameSpaceFunctionNameObject1 CallingObject1, NameSpaceFunctionNameObject2 CallingObject2)
{
// create the request envelope
FunctionName_Request req = new FunctionName_Request();
req.CallingObject1 = CallingObject1;
req.CallingObject2 = CallingObject2;
// make the call
FunctionName_ResponseEnvelope resp = MakeWCFCall<FunctionName_ResponseEnvelope>(_EndpointAddress, _ServerName, req);
// get the response object
Response = resp.Body.FunctionName_Response.Response;
// result
return resp.Body.FunctionName_Response.Result;
}
Finally, this is the function that actually serializes and deserializes the object into the HttpClient. In my case, these are synchronous, but you could easily adapt this to work in the standard async case. It's template so it works with any of the proxy-generated types.
/////////////////////////////////////////////////////////////////////
// make a WCF call using an HttpClient object
// uses the DataContractSerializer to serialze/deserialze the messages from the objects
//
// We manually add the <s:Envelope> and <s:Body> tags. There should be a way to get
// the DataContractSerializer to write these too, but everything I tried gave a message
// that was not able to be procesed by the service. This includes the Message object.
// Deserializing works fine, but serializing did not.
private T MakeWCFCall<T>(string strEndpoint, string strServerName, object SourceObject)
{
T Response = default(T);
string strSoapMessage = "";
string strSoapAction = "";
// get the Soap Action by using the DataContractAttribute's name
// start by getting the list of custom attributes.
// there should only be the one
object[] oaAttr = SourceObject.GetType().GetCustomAttributes(false);
if (oaAttr.Length > 0)
{
// iterate just in case there are more
foreach (DataContractAttribute oAttr in oaAttr)
{
// make sure we got one
if (oAttr != null)
{
// this is the action!
strSoapAction = oAttr.Name;
break;
}
}
}
// serialize the request into a string
// use a memory stream as the source
using (MemoryStream ms = new MemoryStream())
{
// create the DataContractSerializer
DataContractSerializer ser = new DataContractSerializer(SourceObject.GetType());
// serialize the object into the memory stream
ser.WriteObject(ms, SourceObject);
// seek to the beginning so we can read back out of the stream
ms.Seek(0, SeekOrigin.Begin);
// create the stream reader
using (var streamReader = new StreamReader(ms))
{
// read the message back out, adding the Envelope and Body wrapper
strSoapMessage = #"<s:Envelope xmlns:s = ""http://schemas.xmlsoap.org/soap/envelope/""><s:Body>" + streamReader.ReadToEnd() + #"</s:Body></s:Envelope>";
}
}
// now create the HttpClient connection
using (var client = new HttpClient(new NativeMessageHandler()))
{
//specify to use TLS 1.2 as default connection
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
// add the Soap Action header
client.DefaultRequestHeaders.Add("SOAPAction", "http://tempuri.org/" + strServerName + "/" + strSoapAction);
// encode the saop message
var content = new StringContent(strSoapMessage, Encoding.UTF8, "text/xml");
// post to the server
using (var response = client.PostAsync(new Uri(strEndpoint), content).Result)
{
// get the response back
var soapResponse = response.Content.ReadAsStringAsync().Result;
// create a MemoryStream to use for serialization
using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(soapResponse)))
{
// create the reader
// set the quotas
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(
memoryStream,
Encoding.UTF8,
new XmlDictionaryReaderQuotas() { MaxArrayLength = 5000000, MaxBytesPerRead = 5000000, MaxStringContentLength = 5000000 },
null);
// create the Data Contract Serializer
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
// deserialize the response
Response = (T)serializer.ReadObject(reader);
}
}
}
// return the response
return Response;
}
This approach allowed me to quickly write wrappers for all of my WCF service functions, and it's working well so far.

Do I have to use SSL with Paypal? How do I set Paypal on Windows Azure?

I am putting online an old web application I had running like 3 years ago.
Back then everything worked and Paypal's ExpressCheckout was set perfectly.
I really cannot remember what I was doing back then but now I put my app on Windows Azure. My app is written in ASP.NET MVC5.
The following piece of code might look familiar for those of you who implemented Paypal inside your apps and its probably taken from Paypal's documentation and used for posing to Paypal's server:
/// <summary>
/// HttpCall: The main method that is used for all API calls
/// </summary>
/// <param name="NvpRequest"></param>
/// <returns></returns>
public string HttpCall(string NvpRequest) //CallNvpServer
{
string url = pendpointurl;
//To Add the credentials from the profile
string strPost = NvpRequest + "&" + buildCredentialsNVPString();
strPost = strPost + "&BUTTONSOURCE=" + HttpUtility.UrlEncode(BNCode);
HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create(url);
objRequest.Timeout = Timeout;
objRequest.Method = "POST";
objRequest.ContentLength = strPost.Length;
objRequest.ContentType = "application/x-www-form-urlencoded";
try
{
using (StreamWriter myWriter = new StreamWriter(objRequest.GetRequestStream()))
{
myWriter.Write(strPost, 0, strPost.Length);
}
}
catch (Exception e)
{
CommonFuncs.Log(MyGlobals.LOG_FILE_DO_EXPRESS_CHECKOUT, e.Message);
return null;
/*
if (log.IsFatalEnabled)
{
log.Fatal(e.Message, this);
}*/
}
//Retrieve the Response returned from the NVP API call to PayPal
HttpWebResponse objResponse = (HttpWebResponse)objRequest.GetResponse();
string result;
using (StreamReader sr = new StreamReader(objResponse.GetResponseStream()))
{
result = sr.ReadToEnd();
}
//Logging the response of the transaction
/* if (log.IsInfoEnabled)
{
log.Info("Result :" +
" Elapsed Time : " + (DateTime.Now - startDate).Milliseconds + " ms" +
result);
}
*/
return result;
}
Now, when I'm trying to POST (here)
using (StreamWriter myWriter = new StreamWriter(objRequest.GetRequestStream()))
{
myWriter.Write(strPost, 0, strPost.Length);
}
I am getting the following error message
The request was aborted: Could not create SSL/TLS secure channel.
Does it mean that I have to purchase an SSL certificate? or is there something I just need to tweek on Azure so it will work?
No need to purchase an SSL. But you can upgrade your certificate into SHA 256 and TLS 1.2. Refer to the link below.
https://www.paypal-knowledge.com/infocenter/index?page=content&id=FAQ1913
and
https://github.com/paypal/TLS-update
Thank you #PP_MTS_Steven.
I can't remember the source on SO which gave me the solution. However, all I did was to put this two lines of code:
ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
// allows for validation of SSL conversations
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
Right here:
public string HttpCall(string NvpRequest) //CallNvpServer
{
string url = pendpointurl;
//To Add the credentials from the profile
string strPost = NvpRequest + "&" + buildCredentialsNVPString();
strPost = strPost + "&BUTTONSOURCE=" + HttpUtility.UrlEncode(BNCode);
ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
// allows for validation of SSL conversations
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create(url);
objRequest.Timeout = Timeout;
objRequest.Method = "POST";
objRequest.ContentLength = strPost.Length;
objRequest.ContentType = "application/x-www-form-urlencoded";
...
And things started working.

Basic Auth to Receive Token in Spring Security

I am implementing a RESTful API where the user must authenticate. I want the user to POST their credentials in order to receive a JSON web token (JWT), which is then used for the remainder of the session. I have not found any good sources of information to set this up. In particular, I'm having trouble with the filter. Does anybody have any information or tutorials to help me set this up?
The people at Stormpath have quite a straightforward solution for achieving Oauth. Please take a look at Using Stormpath for API Authentication.
As a summary, your solution will look like this:
You will use the Stormpath Java SDK to easily delegate all your user-management needs.
When the user presses the login button, your front end will send the credentials securely to your backend-end through its REST API.
By the way, you can also completely delegate the login/register/logout functionality to the Servlet Plugin. Stormpath also supports Google, Facebook, LinkedIn and Github login.
Your backend will then try to authenticate the user against the Stormpath Backend and will return an access token as a result:
/**
* Authenticates via username (or email) and password and returns a new access token using the Account's ApiKey
*/
public String getAccessToken(String usernameOrEmail, String password) {
ApiKey apiKey = null;
try {
AuthenticationRequest request = new UsernamePasswordRequest(usernameOrEmail, password);
AuthenticationResult result = application.authenticateAccount(request);
Account account = result.getAccount();
ApiKeyList apiKeys = account.getApiKeys();
for (ApiKey ak : apiKeys) {
apiKey = ak;
break;
}
if (apiKey == null) {
//this account does not yet have an apiKey
apiKey = account.createApiKey();
}
} catch (ResourceException exception) {
System.out.println("Authentication Error: " + exception.getMessage());
throw exception;
}
return getAccessToken(apiKey);
}
private String getAccessToken(ApiKey apiKey) {
HttpRequest request = createOauthAuthenticationRequest(apiKey);
AccessTokenResult accessTokenResult = (AccessTokenResult) application.authenticateApiRequest(request);
return accessTokenResult.getTokenResponse().getAccessToken();
}
private HttpRequest createOauthAuthenticationRequest(ApiKey apiKey) {
try {
String credentials = apiKey.getId() + ":" + apiKey.getSecret();
Map<String, String[]> headers = new LinkedHashMap<String, String[]>();
headers.put("Accept", new String[]{"application/json"});
headers.put("Content-Type", new String[]{"application/x-www-form-urlencoded"});
headers.put("Authorization", new String[]{"Basic " + Base64.encodeBase64String(credentials.getBytes("UTF-8"))});
Map<String, String[]> parameters = new LinkedHashMap<String, String[]>();
parameters.put("grant_type", new String[]{"client_credentials"});
HttpRequest request = HttpRequests.method(HttpMethod.POST)
.headers(headers)
.parameters(parameters)
.build();
return request;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Then, for every authenticated request, your backend will do:
/** This is your protected API */
public void sayHello(String accessToken) throws OauthAuthenticationException {
try {
if (verify(accessToken)) {
doStartEngines(); //Here you will actually call your internal doStartEngines() operation
}
} catch (OauthAuthenticationException e) {
System.out.print("[Server-side] Engines not started. accessToken could not be verified: " + e.getMessage());
throw e;
}
}
private boolean verify(String accessToken) throws OauthAuthenticationException {
HttpRequest request = createRequestForOauth2AuthenticatedOperation(accessToken);
OauthAuthenticationResult result = application.authenticateOauthRequest(request).execute();
System.out.println(result.getAccount().getEmail() + " was successfully verified");
return true;
}
private HttpRequest createRequestForOauth2AuthenticatedOperation(String token) {
try {
Map<String, String[]> headers = new LinkedHashMap<String, String[]>();
headers.put("Accept", new String[]{"application/json"});
headers.put("Authorization", new String[]{"Bearer " + token});
HttpRequest request = HttpRequests.method(HttpMethod.GET)
.headers(headers)
.build();
return request;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
All this will not need any special Spring Security configuration, this is plain Java code that you can run in any framework.
Please take a look here for more information.
Hope that helps!
Disclaimer, I am an active Stormpath contributor.
Here's a working sample code from Spring Security OAuth github.
https://github.com/spring-projects/spring-security-oauth/tree/master/tests/annotation/jwt
You probably don't even need to mess with the filters as shown in the above example. If you've custom needs, please post some sample code.

Using Google experimental implementation of OAuth 2.0 to access existing API endpoints

According to this documentation, process of receiving OAuth access token is straightforward. I would like to see a list of all available API endpoints that is ready to accept OAuth 2.0 access token. But for my current needs i would like to somehow receive username and email of a user using OAuth 2.0 access token.
I successfully can receive, for example, data from this endpoint:
https://www.google.com/m8/feeds/contacts/default/full
But unable to receive data from this endpoint:
https://www.googleapis.com/userinfo/email
I tried both header-base and querystring-base approaches of passing single access token. Here is a header i tried:
Authorization: OAuth My_ACCESS_TOKEN
And I even tried OAuth 1.0 version of Authorization header, but... in OAuth 2.0 we do not have secret access token, for instance. Google use bearer tokens in his implementation of OAuth 2.0, so no additional credentials are required.
Anyone successfully received username and email using Google OAuth 2.0?
I found the answer I was looking for. I had to convert PHP to MVC, but pretty easy:
http://codecri.me/case/430/get-a-users-google-email-address-via-oauth2-in-php/
My MVC Login sandbox code looks like the following.
(using JSON.Net http://json.codeplex.com/)
public ActionResult Login()
{
string url = "https://accounts.google.com/o/oauth2/auth?";
url += "client_id=<google-clientid>";
url += "&redirect_uri=" +
// Development Server :P
HttpUtility.UrlEncode("http://localhost:61857/Account/OAuthVerify");
url += "&scope=";
url += HttpUtility.UrlEncode("http://www.google.com/calendar/feeds/ ");
url += HttpUtility.UrlEncode("http://www.google.com/m8/feeds/ ");
url += HttpUtility.UrlEncode("http://docs.google.com/feeds/ ");
url += HttpUtility.UrlEncode("https://mail.google.com/mail/feed/atom ");
url += HttpUtility.UrlEncode("https://www.googleapis.com/auth/userinfo.email ");
url += HttpUtility.UrlEncode("https://www.googleapis.com/auth/userinfo.profile ");
url += "&response_type=code";
return new RedirectResult(url);
}
The code returned is proof of Authorization token from the user, which then needs to be turn into a Authentication (accessToken) to access resources.
My MVC OAuthVerify then looks like:
public ActionResult AgentVerify(string code)
{
JObject json;
if (!string.IsNullOrWhiteSpace(code))
{
NameValueCollection postData = new NameValueCollection();
postData.Add("code", code);
postData.Add("client_id", "<google-clientid>");
postData.Add("client_secret", "<google-client-secret>");
postData.Add("redirect_uri", "http://localhost:61857/Account/OAuthVerify");
postData.Add("grant_type", "authorization_code");
try
{
json = JObject.Parse(
HttpClient.PostUrl(
new Uri("https://accounts.google.com/o/oauth2/token"), postData));
string accessToken = json["access_token"].ToString();
string refreshToken = json["refresh_token"].ToString();
bool isBearer =
string.Compare(json["token_type"].ToString(),
"Bearer",
true,
CultureInfo.CurrentCulture) == 0;
if (isBearer)
{
json = JObject.Parse(
HttpClient.GetUrl(
new Uri("https://www.googleapis.com/oauth2/v1/userinfo?alt=json"),
accessToken));
string userEmail = json["email"].ToString();
}
return View("LoginGood");
}
catch (Exception ex)
{
ErrorSignal.FromCurrentContext().Raise(ex); //ELMAH
}
}
return View("LoginBad");
}
To complete how everything works, I've included the HttpClient utility I created in case anyone needed it.
public class HttpClient
{
public static string GetUrl(Uri url, string OAuth)
{
string result = string.Empty;
using (WebClient httpClient = new WebClient())
{
httpClient.Headers.Add("Authorization","OAuth " + OAuth);
result = httpClient.DownloadString(url.AbsoluteUri);
}
return result;
}
public static string PostUrl(Uri url, NameValueCollection formData)
{
string result = string.Empty;
using (WebClient httpClient = new WebClient())
{
byte[] bytes = httpClient.UploadValues(url.AbsoluteUri, "POST", formData);
result = Encoding.UTF8.GetString(bytes);
}
return result;
}
}
Again, this is test code just to get it to function, I do not recommend using this as-is in a production environment.
try this:
curl -k https://www.googleapis.com/userinfo/email -H "Authorization: OAuth 1/g5_039aCIAfEBuL7OCyB31n1URYU5tUIDudiWKuxN1o"
output: email=name#gmail.com&isVerified=tru