Edited Thank you #Marco
I'm trying to write a function app that grabs an SVG from a URL and converts it to PNG. I know there are existing API's that do this like CloudConvert, but they don't work nicely with embedded fonts, which is a requirement for me.
Anyway, I wrote a very basic function app that simply downloads a file at this point. Everything works perfectly fine locally, but when I publish to Azure, I get An exception occurred during a WebClient request.
Thanks to #Marco's suggestion, I switched from WebClient to HTTPWebRequest to get more detailed error handling, and as a result, I see the following:
2018-10-11T13:53:53.558 [Info] Function started (Id=e3cbda04-140e-4ef7-ad6c-c871ffe179dd)
2018-10-11T13:53:53.590 [Info] C# HTTP trigger function processed a request.
2018-10-11T13:53:53.752 [Info] Download Fail
2018-10-11T13:53:53.752 [Info] Access to the path
'D:\Windows\system32\734e16961fc276df.svg' is denied.
Am I trying to do something that isn't possible, or is there a fix for this? Is there a way to configure permissions in an Azure function? I need to pull the file down to edit and not just work with the byte array.
Many thanks!
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req,
TraceWriter log, ExecutionContext context)
{
log.Info("C# HTTP trigger function processed a request.");
// parse query parameter
string svgURL = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "l", true) == 0)
.Value;
if (svgURL == null)
{
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
svgURL = data?.svgURL;
}
// download file from URL
var uniqueName = GenerateId() ;
try
{
using (var client = new WebClient())
{
client.DownloadFile(svgURL, uniqueName + ".svg" );
}
}
catch (Exception e)
{
log.Info("Download Fail");
log.Info(e.Message);
}
}
The easiest way to solve this is to use temp storage. I can see why Azure wouldn't want functions cludging up the app directory anyway. Updated code below:
I replaced this:
client.DownloadFile(svgURL, uniqueName + ".svg" );
With this:
client.DownloadFile(svgURL, Path.GetTempPath() + "\\" + uniqueName + ".svg" );
Worked like a charm.
Edit:
Below is the GitHub repo where I make this call. There's other stuff going on but you can see where I save to temp storage.
https://github.com/osuhomebase/SVG2PNG-AzureFunction
Related
I have created an azure function (HTTP triggered with Open API) and deployed to azure. The endpoints working good. I am getting response when testing from Postman
Swagger UI also loading. But when trying to post from Swagger it keeps saying 401 unauthorized. But I have copied the function key from Azure portal (below screen shot) and specified that in authorize popup with in swagger.
But it still saying unauthorized.
When I copy the URL from portal for the http endpoint, it looks like this
https://myurls-asev3.appserviceenvironment.net/api/ObjectRead?code=mycode
Here mycode is exactly the same one I copied from function keys. But the only difference is, the code is attached as a query string in url when I copied the URL from portal
But in swagger it sends as header.
But in function configuration I designed it to accept as header.
[FunctionName("ObjectRead")]
[OpenApiOperation(operationId: "Run", tags: new[] { "name" })]
[OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Header)]
[OpenApiRequestBody("application/json", typeof(FileDetails))]
[OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(string), Description = "The OK response")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req)
{
_logger.LogInformation($"ObjectRead function triggered at {DateTime.UtcNow.ToString("O")}.");
var requestBody = await new StreamReader(req.Body).ReadToEndAsync().ConfigureAwait(false);
var data = JsonConvert.DeserializeObject<FileDetails>(requestBody);
var responseMessage = await _objectReadService.ReadAsync(data.FileName, data.FilePath).ConfigureAwait(false);
_logger.LogInformation($"ObjectRead function completed at {DateTime.UtcNow.ToString("O")}.");
return responseMessage.Length != 0 ? new FileStreamResult(responseMessage, "application/octet-stream") { FileDownloadName = data.FileName} : new NotFoundObjectResult("Unable to retrieve the file or File not found.");
}
Above code snippet 3rd line I have mentioned the key as header
Even why its not working in swagger and in url why the code still shows as query string
From the documentation:
https://<APP_NAME>.azurewebsites.net/api/<FUNCTION_NAME>?code=<API_KEY>
The key can be included in a query string variable named code, as above. It can also be included in an x-functions-key HTTP header. The value of the key can be any function key defined for the function, or any host key.
so your attirbute should looks like that:
[OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "x-functions-key", In = OpenApiSecurityLocationType.Header)]
I created an Azure function which uses a packages that needs a properties file in order to work. From the way they implemented this I need to pass the property file location to the package.
ConfigFactory.setConfigLocation("SomeFolder/AnotherFolder/connector.properties");
When I run this locally this works just fine and everything works as intended. However when I publish it on Azure it tells me the file url is invalid,
[Error] Invalid url to location ]SomeFolder/AnotherFolder/connector.properties[ errorMessage :no protocol: SomeFolder/AnotherFolder/connector.properties file
Am I missing something? How come this doesn't just work on Azure?
Refer to a file in an azure function you have 2 options.
Using Execution Context :
[FunctionName("MyNewHTTPAzureFunction")]
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext context)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name) ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response." : $"Hello, {name}. This HTTP triggered function executed successfully.";
//This is how you can access the curent Directory with the help of context.FunctionAppDirectory
string localFile = Path.Combine(context.FunctionAppDirectory, "Data", "Your_file_with_extension");
string readLocalFile = File.ReadAllText(localFile);
...... // your business needs
return new OkObjectResult(responseMessage);
}
Use Path.GetDirectoryName :
To access the directory of the Azure Function if you don’t have access to the ExecutionContext.
You can read the local file in your Startup class like below.
var getPath= Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var rootpath = Path.GetFullPath(Path.Combine(binpath, ".."));
///Read the file
File.ReadAllText(rootpath + "/path/to/filename_with_extension");
Azure Function Access To The Path Is Denied
To avoid the Access Denied error in Azure functions, You Always try to keep your files in the D:\home\site\wwwroot (root Directory) directory that is exactly where exactly the Azure Function resides.
Refer for more information here & Doc for Binding Expression pattern
I have a client using HttpClient.GetAsync to call into a Azure Function Http Trigger in .Net 5.
When I call the function using PostMan, I get my custom header data.
However, when I try to access my response object (HttpResponseMessage) that is returned from HttpClient.GetAsync, my header data empty.
I have my Content data and my Status Code. But my custom header data are missing.
Any insight would be appreciated since I have looking at this for hours.
Thanks for you help.
Edit: Here is the code where I am making the http call:
public async Task<HttpResponseMessage> GetQuotesAsync(int? pageNo, int? pageSize, string searchText)
{
var requestUri = $"{RequestUri.Quotes}?pageNo={pageNo}&pageSize={pageSize}&searchText={searchText}";
return await _httpClient.GetAsync(requestUri);
}
Edit 8/8/2021: See my comment below. The issue has something to do with using Blazor Wasm Client.
For anyone having problems after following the tips on this page, go back and check the configuration in the host.json file. you need the Access-Control-Expose-Headers set to * or they won't be send even if you add them. Note: I added the "extensions" node below and removed my logging settings for clarity.
host.json (sample file):
{
"version": "2.0",
"extensions": {
"http": {
"customHeaders": {
"Access-Control-Expose-Headers": "*"
}
}
}
}
This is because HttpResponseMessage's Headers property data type is HttpResponseHeaders but HttpResponseData's Headers property data type is HttpHeadersCollection. Since, they are different, HttpResponseHeaders could not bind to HttpHeadersCollection while calling HttpClient.GetAsync(as it returns HttpResponseMessage).
I could not find a way to read HttpHeadersCollection through HttpClient.
As long as your Azure function code is emitting the header value, you should be able to read that in your client code from the Headers collection of HttpResponseMessage. Nothing in your azure function (which is your remote endpoint you are calling) makes it any different. Remember, your client code has no idea how your remote endpoint is implemented. Today it is azure functions, tomorrow it may be a full blown aspnet core web api or a REST endpoint written in Node.js. Your client code does not care. All it cares is whether the Http response it received has your expected header.
Asumming you have an azure function like this where you are adding a header called total-count to the response.
[Function("quotes")]
public static async Task<HttpResponseData> RunAsync(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
FunctionContext executionContext)
{
var logger = executionContext.GetLogger("Quotes");
logger.LogInformation("C# HTTP trigger function processed a request for Quotes.");
var quotes = new List<Quote>
{
new Quote { Text = "Hello", ViewCount = 100},
new Quote { Text = "Azure Functions", ViewCount = 200}
};
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("total-count", quotes.Count.ToString());
await response.WriteAsJsonAsync(quotes);
return response;
}
Your existing client code should work as long as you read the Headers property.
public static async Task<HttpResponseMessage> GetQuotesAsync()
{
var requestUri = "https://shkr-playground.azurewebsites.net/api/quotes";
return await _httpClient.GetAsync(requestUri);
}
Now your GetQuotesAsync method can be called somewhere else where you will use the return value of it (HttpResponseMessage instance) and read the headers. In the below example, I am reading that value and adding to a string variable. HttpResponseMessage implements IDisposable. So I am using a using construct to implicitly call the Dispose method.
var msg = "Total count from response headers:";
using (var httpResponseMsg = await GetQuotesAsync())
{
if (httpResponseMsg.Headers.TryGetValues("total-count", out var values))
{
msg += values.FirstOrDefault();
}
}
// TODO: use "msg" variable as needed.
The types which Azure function uses for dealing with response headers is more of an implementation concern of azure functions. It has no impact on your client code where you are using HttpClient and HttpResponseMessage. Your client code is simply dealing with standard http call response (response headers and body)
The issue is not with Blazor WASM, rather if that header has been exposed on your API Side. In your azure function, add the following -
Note: Postman will still show the headers even if you don't expose the headers like below. That's because, Postman doesn't care about CORS headers. CORS is just a browser concept and not a strong security mechanism. It allows you to restrict which other web apps may use your backend resources and that's all.
First create a Startup File to inject the HttpContextAccessor
Package Required: Microsoft.Azure.Functions.Extensions
[assembly: FunctionsStartup(typeof(FuncAppName.Startup))]
namespace FuncAppName
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddScoped<HttpContextAccessor>();
}
}
}
Next, inject it into your main Function -
using Microsoft.AspNetCore.Http;
namespace FuncAppName
{
public class SomeFunction
{
private readonly HttpContext _httpContext;
public SomeFunction(HttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
[FunctionName("SomeFunc")]
public override Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, new[] { "post" }, Route = "run")] HttpRequest req)
{
var response = "Some Response"
_httpContext.Response.Headers.Add("my-custom-header", "some-custom-value");
_httpContext.Response.Headers.Add("my-other-header", "some-other-value");
_httpContext.Response.Headers.Add("Access-Control-Expose-Headers", "my-custom-header, my-other-header");
return new OkObjectResult(response)
}
If you want to allow all headers you can use wildcard (I think, not tested) -
_httpContext.Response.Headers.Add("Access-Control-Expose-Headers", "*");
You still need to add your web-app url to the azure platform CORS. You can add * wildcard, more info here - https://iotespresso.com/allowing-all-cross-origin-requests-azure-functions/
to enable CORS for Local Apps during development - https://stackoverflow.com/a/60109518/9276081
Now to access those headers in your Blazor WASM, as an e.g. you can -
protected override async Task OnInitializedAsync()
{
var content = JsonContent.Create(new { query = "" });
using (var client = new HttpClient())
{
var result = await client.PostAsync("https://func-app-name.azurewebsites.net/api/run", content);
var headers = result.Headers.ToList();
}
}
How can I add a new document to Content Server 10.5 using the REST api?
I am following the Swagger docs for creating a node, but it is not clear how I attach the file to the request. Here is (roughly) the code I am using:
var folderId = 2000;
var docName = "test";
var uri = $"http://[serverName]/otcs/llisapi.dll/api/v1/nodes?type=144&parent_id={folderId}&name={docName}";
var request = new HttpRequestMessage();
request.Headers.Add("Connection", new[] { "Keep-Alive" });
request.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate");
request.Headers.Add("Pragma", "no-cache");
request.Headers.Add("OTCSTicket", /* ticket here */);
request.RequestUri = new Uri(uri);
request.Method = HttpMethod.Post;
request.Content = new ByteArrayContent(data);
request.Content.Headers.ContentType = new MediaTypeHeaderValue(MimeMapping.GetMimeMapping(filePath));
request.Headers.ExpectContinue = false;
var httpClientHandler = new HttpClientHandler
{
Proxy = WebRequest.GetSystemWebProxy(),
UseProxy = true,
AllowAutoRedirect = true
};
using (var client = new HttpClient(httpClientHandler))
{
var response = client.SendAsync(request).Result;
IEnumerable<string> temp;
var vals = response.Headers.TryGetValues("OTCSTicket", out temp) ? temp : new List<string>();
if (vals.Any())
{
this.ticket = vals.First();
}
return response.Content.ReadAsStringAsync().Result;
}
I've been searching through the developer.opentext.com forums, but finding a complete example in c# is proving tough - there are a few examples in javascript, but attempting to replicate these in c# or via chrome or firefox extensions just give the same results. Calling other CS REST methods has not been an issue so far, this is the first one that's giving me problems.
Edit: I pasted the wrong url into my question, which I've now fixed. It was var uri = $"http://[serverName]/otcs/llisapi.dll/api/v1/forms/nodes/create?type=0&parent_id={folderId}&name={docName}";.
Your URL doesn't look like the REST API, it's rather the traditional URL used for the UI.
This article should describe how to do what you want to do:
https://developer.opentext.com/webaccess/#url=%2Fawd%2Fresources%2Farticles%2F6102%2Fcontent%2Bserver%2Brest%2Bapi%2B%2Bquick%2Bstart%2Bguide&tab=501
EDITED:
Ok, so that's how it should work:
send a POST to http://www.your_content_server.com/cs[.exe]/api/v1/nodes
send this in your payload to create a document in your enterprise workspace
type=144
parent_id=2000
name=document_name.txt
<file>
A incomplete demo in Python would look like this. Make sure you get a valid ticket first.
files = {'file': (open("file.txt", 'rb')}
data = { 'type': 144, 'parent_id': 2000, 'name': 'document_name.txt' }
cs = requests.post(url, headers={'otcsticket':'xxxxxxx'}, data=data, files=files)
if cs.status_code == 200:
print "ok"
else:
print cs.text
You will need a form input to get the file onto the page then you can use filestreams to redirect it, there is great guide for that here.
Reading files in JavaScript using the File APIs
Here is a Jquery/ Ajax example.
I find the best way to go about this is to use Postman (Chrome Plugin) to experiment until you get comfortable.
var form = new FormData();
form.append("file", "*filestream*");
form.append("parent_id", "100000");
form.append("name", "NameYourCreatedFile");
form.append("type", "144");
var settings = {
"async": true,
"url": "/cs.exe/api/v1/nodes", // You will need to amend this to match your environment
"method": "POST",
"headers": {
"authorization": "Basic **use Postman to generate this**",
"cache-control": "no-cache",
},
"processData": false,
"contentType": false,
"mimeType": "multipart/form-data",
"data": form
}
$.ajax(settings).done(function (response) {
console.log(response);
});
It appears that the OpenText API only supports file uploads through asynchronous JavaScript uploads - not through traditional file uploads by using typical posted form requests that contain the files contents (which is pretty crappy to be honest - as this would be the easiest to handle on server side).
I've contacted their support and they were absolutely no help - they said since it's working with JavaScript, then they can't help me. Anyone else utilizing any language besides JavaScript is SOL. I submitted my entire API package, but they didn't bother investigating and wanted to close my ticket ASAP.
The only way I've found to do this, is to upload / send the file into your 'Upload' directory on your Content Servers web server (on ours it was set to D:\Upload).This directory location is configurable in the admin section.
Once you've sent the file to your web server, send a create node request with the file param set to the full file path of the file residing on your server, as the OpenText API will attempt to retrieve the file from this directory.
I've created a PHP API for this, and you can browse its usage here:
https://github.com/FBCLIT/OpenTextApi
<?php
use Fbcl\OpenTextApi\Client;
$client = new Client('http://server.com/otcs/cs.exe', 'v1');
$api = $client->connect('username', 'secret');
try {
// The folder node ID of where the file will be created under.
$parentNodeId = '12356';
// The file name to display in OpenText
$fileName = 'My Document.txt';
// The actual file path of the file on the OpenText server.
$serverFilePath = 'D:\Upload\My Document.txt';
$response = $api->createNodeDocument($parentNodeId, $fileName, $serverFilePath);
if (isset($response['id'])) {
// The ID of the newly created document will be returned.
echo $response['id'];
}
} catch (\Exception $ex) {
// File not found on server drive, or issue creating node from given parent.
}
MIME Type detection appears to happen automatically, and you do not need to send anything for it to detect the file type. You can name the file to whatever you like without an extension.
I have also discovered that you cannot use an IP address or Host name for uploading files in this manor. You must enter a path that is local to the server you are uploading to. You can however give just the file name that exists in the Upload directory, and the OpenText API seems to locate it fine.
For example, you can pass either D:\Uploads\Document.txt or Document.txt.
If you haven't done it correctly, you should get the error:
Client error: POST http://server.com/otcs/cs.exe/api/v1/nodes resulted in a 400 Bad Request response: {"error":"Error: File could not be found within the upload directory."}
I am trying again to login to instagram, but i have to do this without using their api v2 which uses oAuth for verifying.
The only thing i have is Username and Password to login.
So what i did was looked for the way, instagram application for Android and hopefully IOS does that.
I see that it creates a request at the following url:
https://instagr.am/api/v1/accounts/login/
If you would visit that link directly, you will more likely get a error saying that the page couldn't be find. After a little bit of googling, i came across a 2 years old post which states that in the Useragent, we have to contain the string "Instagram" for it to work.
I did that by faking the useragent and the result is as follows:
{"status":"fail","message":"Your version of Instagram is out of date. Please upgrade your app in the Play Store to log in to Instagram."}
Now, i am guessing that we also need to add something else or some other headers too, so i was looking for a way to grab the request being sended to instagram by either of their Android or IOS app.
So next i downloaded Bluestack and installed and ran instagram on my computer using this. I was able to install and login it successfully, but then i was unable to log it using Charles since the request is being sent to https:// server
I also tried to grab it through Wireshark but unfortuantely, i am not much experienced in using it and hence don't know it purpose.
So could anyone help me to get how to login to instagram with C# without using oAuth, cuz i just have username and password.
I would probably code the end part of requesting myself, but i am unable to capture the headers being sent to instagram.
Also, if there is anything like Charles/Wireshark which captures network traffic for Android, do let me know about it too.
You should be able to capture the request and see the request headers using Fiddler if you can tell your device to use your pcs internet connection by going through USB. It seems your getting close but might just need a version number somewhere in your request.
Yeah you can do this using Webview
Here you go
mWebView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
UrlQuerySanitizer.ValueSanitizer sanitizer = UrlQuerySanitizer.getAllButNulLegal();
// remember to decide if you want the first or last parameter with the same name
// If you want the first call setPreferFirstRepeatedParameter(true);
sanitizer.sanitize(url);
String value = sanitizer.sanitize("username"); // get your value
if(MyBridge.getUsername()!=null)username = MyBridge.getUsername();
return true;
}
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
// showLoading();
CookieManager.getInstance().removeAllCookies(null);
// CookieManager.getInstance().flush();
progressBar.setVisibility(View.VISIBLE);
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
progressBar.setVisibility(View.GONE);
if (url.equalsIgnoreCase(mURL)) {
view.addJavascriptInterface(new MyBridge(InstagramOfficalLoginActivity.this), "bridge");
String javascript = "javascript: document.getElementsByClassName(\"_0mzm- sqdOP L3NKy \")[0].onclick = function() {\n" +
" var username = document.getElementsByName(\"username\").value;\n" +
" var password = document.getElementsByName(\"password\").value;\n" +
" bridge.saveData(username, password);\n" +
" };";
view.loadUrl(javascript);
}
if (isSessionid ) {
// username = MyBridge.getUsername();
//сохранение данных пользователя
Logins logins = new Logins();
logins.setUserId(InstaUtils.getUserId());
logins.setUserName("");
logins.setProfilePic("");
logins.setSession_id(InstaUtils.getSessionid());
logins.setCooki(InstaUtils.getCookies());
logins.setCsrf(InstaUtils.getCsrf());
long id = DataObjectRepositry.dataObjectRepositry.addNewUser(logins);
PreferencesManager.savePref(GlobalConstant.USERNAME,username);
PreferencesManager.savePref(GlobalConstant.USER_ID, InstaUtils.getUserId());
PreferencesManager.savePref(GlobalConstant.TOKEN, InstaUtils.getSessionid());
PreferencesManager.savePref(GlobalConstant.PROFILE_PIC,"");
Intent intent = new Intent(InstagramOfficalLoginActivity.this, MainActivity.class);
PreferencesManager.savePref("isLogin",true);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("user", InstaUtils.getUserId());
intent.putExtra("database_id",String.valueOf(id));
mWebView.destroy();
mWebView = null;
startActivity(intent);
}
}
#Override
public void onLoadResource(WebView view, String url) {
super.onLoadResource(view, url);
progressBar.setVisibility(View.GONE);
cookies = CookieManager.getInstance().getCookie(url);
try {
String session_id = getCookie(url, "sessionid");
String csrftoken = getCookie(url, "csrftoken");
String userid = getCookie(url, "ds_user_id");
if (session_id != null && csrftoken != null && userid != null) {
isSessionid = true;
InstaUtils.setSessionId(session_id);
InstaUtils.setUserId(userid);
InstaUtils.setCookies(cookies);
InstaUtils.setCsrf(csrftoken, cookies);
}
}catch (Exception e){
e.printStackTrace();
}
}
#SuppressWarnings("deprecation")
#Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
ToastUtils.ErrorToast(InstagramOfficalLoginActivity.this, description);
}
#TargetApi(android.os.Build.VERSION_CODES.M)
#Override
public void onReceivedError(WebView view, WebResourceRequest req, WebResourceError rerr) {
// Redirect to deprecated method, so you can use it in all SDK versions
onReceivedError(view, rerr.getErrorCode(), rerr.getDescription().toString(), req.getUrl().toString());
}
});
mWebView.loadUrl(mURL);
You can also download fully working code from my github profile Here is the link.
I hope it work,Thanks :)
you can always login through https://instagram.com/accounts/login/ having only login\password.
having ability to sniff traffic will not help you, as they sign all their messages now to prevent things you are trying to achieve.
You can find Instaguser library at here. https://github.com/ThinhVu/InstagramUser
Instaguser allow you login into instagram without OAuth.
At the moment, you can change user information: username, biography, ... etc.