The Golden Spot

I hope this helps someone who is learning about Linux and web application programming with Python, Django, and Javascript.

Friday, November 25, 2011

Authentication redirects with iOS and Django

I am writing a client/server application. The client is an iPhone; client requests are handled by a Django application. Below, I will demonstrate how an iOS device can respond to redirects for login credentials from a Django service when a session has expired. I have not found much written about this subject so I am publishing what I have learned. This assumes that you know how to store and retrieve the username and password on an iOS device via Keychain Services, make HTTP requests from an iOS client, and create basic Django applications.

There are several questions that arise when acquiring a new session id with the client:
  1. How do we send username and password credentials to the Django service from the iOS client, such that we a) login to the server b) get forwarded to the original page we requested?
  2. What happens when the client requests a Django view with an @login_required decorator if the client's session id is expired?
  3. What happens to POST data in an original client request after the server sends login URL redirects while sending authentication challenges?
I hope the following post answers all of these. First, some definitions:
  • 'session id' - this refers to the hexadecimal string sent from the server as a cookie value, after the a user has signed-in/logged-in. (eg. "2b1189a188b44ad18c35e113ac6ceead"). More info on Django sessions
  • 'server' - in this case a Django app, hosted by Apache using mod_wsgi.
  • 'client' - in this case an iPhone (3GS), running the application and sending requests over a LAN. But theoretically, it could be any iOS networked device.
I will demonstrate with a URL that requires the user to be 'logged in' on the Django app. If the client does not have a valid session id, Django will respond by forwarding to a login view. In our case we will challenge the iOS client with a WWW-Authenticate; the client will respond to the challenge in a delegate method and be redirected to the original URL requested by the client. Inside a delegate method on the iOS client, I will show how to preserve the original request's HTTP method and (NSData *)HTTPBody .

Let's make this a simple HTTP GET request. In this case the requested URL will be:

http://10.0.0.2/user/home/


When the client sends the above request, the Django view will detect that the client is not logged in because either the session id was not sent with the GET request or the session id was expired. The server will respond with a status code of 302. In this case the server will respond with the redirect URL of the login page. Here is the @login_required decorator in the requested view:

Before we look at the Django sign in view, let's have a look at the NSURLConnection Delegate in the class that sends the request:

The delegate method is sending a new request containing the redirected URL if the server sends a redirect. And it is sending the original request if no redirect has been sent by the server. It is also sending a version of the original( very first ) request ( HTTPBody, HTTP method, and the latest header fields), only if the redirected URL is the same as the original request; This is how we keep any POST data in the HTTPBody when login redirects have succeeded. More on that below. Let's first take a look at the Django login view:


Notice how we send an authentication challenge from the view if no credentials have been sent by the client? Also, note that within httpd.conf, WSGIPassAuthorization must be set to 'on' for mod_wsgi to forward the credentials to the Django view.

Upon redirecting to the login URL, the client is presented with an authentication challenge. The client should respond appropriately within the NSURLConnection delegate method:
 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge


The client requests the redirected URL and sends the login credentials when receiving a WWW-Authentication challenge. Upon successful login, they are redirected to the original URL- which is stored by Django in the requests as 


request.GET['next'] = "/user/home/"


In the redirect for the original URL, after successful login, the Django view sends the cookie containing the session id. In the NSURLConnection delegate 


-(NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response;

all of the headers from the last request are copied to the newRequest; this includes the session id. If there was any POST data in the original request, it will also be copied to the newRequest, so that the original transaction data will be sent to the server after authenticating and acquiring a new session id.


I hope that this shed some light on how to keep your iOS app able to handle authentication redirects when the server session id expires. 


NOTE: I did not cover displaying a login view that allows the user to enter in username and password in the iOS client if the server login fails. Also, I did not show how to use the 

Labels: , , , , , ,

Wednesday, September 21, 2011

Forward LAN traffic to localhost web server on the same MacBook Pro

Using a stock, early 2011 model MacBook Pro, I needed to access my secure webserver, which runs on localhost and the default port for HTTPS of 443 (127.0.0.1:443), with remote clients on the LAN. The LAN at my home office is 10.0.0.0/24.

The following assumes you have edited httpd.conf and have HTTPS configured correctly; that is, you are able to  point a web browser to https://127.0.0.1/ and see the site you are serving with httpd.


1) Turn on Web Sharing in System Preferences/Sharing. This will open a port on the LAN for httpd to serve requests.
2) Turn on Remote Login in System Preferences/Sharing. This will start sshd to enable port forwarding with ssh at the command line.
3) Using a shell enter:
ssh -NL 10.0.0.29:8080:127.0.0.1:443 your_username@localhost

a ) 10.0.0.29 is the address of my laptop's interface on the LAN.
b ) 8080 is the port that ssh will listen to and then forward traffic to and from.
c ) 127.0.0.1:443 is the address:port on my laptop that ssh will forward traffic to, when it receives traffic on 10.0.0.29:8080
d ) your_username@localhost is the login for the local sshd service that was started by enabling Sharing/Remote Login in System Prefernces

By visiting https://10.0.0.29:8080/ on the 10.0.0.0/24 network with say, an iPhone, you will be able to see the website that is configured in httpd as 127.0.0.1:443 on the MacBook. This helps with local development since you don't have to create a new cert each time your dynamic LAN address changes; it is only a matter of changing the address and port that ssh will forward to localhost.


Monday, May 23, 2011

My UITableView crashes when scrolling and dequeueReusableCellWithIdentifier: is called, during the _second time_ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath is called ( to re-use the cell, after creation ).

I was getting a EXEC_BAD_ACCESS at dequeueReusableCellWithIdentifier:

I realized that after initializing my custom UITableViewCell, I am setting cell = [myCustomCell autorelease] when I should be using cell = [myCustomCell retain].

after customizing a UITableViewCell, call retain, not autorelease, before returning it from tableView:cellForRowAtIndexPath. I'll also mention that I am initializing my custom cell from a nib file by using [[NSBundle mainBundle] loadNibNamed:@"MyCustomCell" owner:self options:nil];

Labels: , ,

Sunday, April 17, 2011

I just learned how to replace newlines with other characters in Emacs. I found Jeremy's blog post from 2007.

I type:

M-x replace-string <return>
C-q C-j
replacement_string_of_choice <return>

C-q is emacs' quoted-insert, which works for inserting control characters;
C-j is the function newline-and-indent which inserts a newline and indents to your major mode.

C-q also works in Xcode as described in this Stack Overflow question

Labels: ,

Saturday, October 31, 2009

pg_dump fails:


$ pg_dump -o -U mydbuser mydb > backup.db.data

$ pg_dump: [archiver (db)] connection to database "mydb" failed: FATAL: password authentication failed for user "mydbuser"


My /var/lib/pgsql/data/pg_hba.conf :


# "local" is for Unix domain socket connections only
local all all ident sameuser
#
host all all 127.0.0.1/32 md5

I thought that since I am logged into the postmaster machine, I would be able to enter the mydbuser database password and execute the command.

According to:

http://www.postgresql.org/docs/8.1/interactive/app-pgdump.html

the option -h is the following:

-h host
--host=host

Specifies the host name of the machine on which the server is running. If the value begins with a slash, it is used as the directory for the Unix domain socket. The default is taken from the PGHOST environment variable, if set, else a Unix domain socket connection is attempted.


Since I did not use -h flag in the command, pg_dump was using a Unix socket to connect the the database and therefore using the IDENT authentication option. I should could have said -h 127.0.0.1 in my pg_dump command- like so:


$ pg_dump -o -U mydbuser -h 127.0.0.1 mydb > backup.db.data


so that I could authenticate using the md5 database user password at the prompt.

Labels: ,

Wednesday, October 21, 2009

After I create a new frame by typing

M-x 5 2

in Emacs, my cursor color is black. But the color of my original frame is LimeGreen or lime green.

I found this site:
http://www.dansanderson.com/blog/mt/mt-search.fcgi?tag=NewbieEmacsTip&blog_id=1&IncludeBlogs=1

and added this line:

(add-to-list 'default-frame-alist '(cursor-color . "lime green")

to my ~/.emacs file

Monday, October 19, 2009

My first django snippet regarding 'very archive'. I am sure I spent too much time reinventing an archive view but I could not figure out how to display all posts in the template, the way I wanted, using generic views!!

The template logic:
http://www.djangosnippets.org/snippets/1765/

The view code:
http://www.djangosnippets.org/snippets/1766/

Labels: ,