January 8, 2020 12:00

Just for fun, I've added "dark mode" to my website.

Specifically the homepage, resume, blog, photography, soccer, and about pages.

The only page that currently may still blind you is "My Ferrets"

It automatically adjusts based on your device's settings. Try it out!

Website

December 20, 2018 11:48

I learn best by doing: jumping right into code, trying out new languages and libraries on my own personal projects.

André Schneider published an excellent project on GitHub a few years ago which demonstrates a lot of Facebook pop's animation capabilities as well as some native iOS easing and other effects.

The only "problem" is that it's written in Objective-C, which I never really got into. I haven't found any great pop demos or tutorials in Swift, but thankfully it's not terribly difficult to convert Objective-C into Swift.

I was able to incorporate some animation effects into my guess the car game but I always wanted to sit down and port the entire demo project over.

Well that is finally done and it can be found here:

https://github.com/andrewtweber/popping-swift

Besides the grunt work of converting the code syntax, I actually learned quite a bit about programmatic constraints (I had always used Storyboards previously), attributed strings, and gesture recognizers. I’m more familiar with Objective-C now too of course.

And since this is a public open-source project that I'm publishing, it also forced me to stick to some good habits such as using guard everywhere and MARK: - comments.

iOS, Swift

January 13, 2016 14:44

Laravel 5.2 has much better support for allowing multiple methods of authentication.

For example, you may want to authorize users with a username and password on the website, but with a random token string on the API.

Setting up tokens is simple enough, assuming you have all of the default config. First you need to add a column called api_token to your users table. It can be any length but the longer it is, the more secure.

Here is an example migration:

Schema::table('users', function (Blueprint $table) {
     $table->char('api_token', 60)->nullable()->after('remember_token');
});

Then you need to make sure that the api guard is specified for any API routes. Unfortunately, Laravel is not able to determine this by itself; it simply defaults to the web guard.

Route::group(['middleware' => ['auth:api']], function()
{
    // API routes here
});

Finally you just need to pass this api_token when sending an API request. There are multiple ways you can do this. Check out the TokenGuard class to see where I'm getting this:

  • as a GET parameter named api_token
  • as an Authorization header (Authorization: Bearer [api_token])
  • or as an auth password

Here is how you would use each of those methods with Guzzle:

$guzzle = new GuzzleHttp\Client();
$guzzle->request($url, ['api_token' => $api_token]);
$guzzle->request($url, [], ['headers' => ['Authorization' => 'Bearer ' . $api_token]]);
$guzzle->request($url, [], ['auth' => [$username, $api_token]]);

The bearer token may take some additional setup. Apache sometimes strips the Authorization token from requests, so you need to add these two lines to your public/.htaccess file:

RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

Finally, of course, you need to generate an api_token for every user that is using the API. Best to do this when creating the user.

Note about upgrades

If you're upgrading from Laravel 5.1 or earlier, chances are you'll need to update some other files.

If you followed the upgrade instructions, you should have already added an "api" guard with a "token" driver to config/auth.php:

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
    ],

You may also need to update your Authenticate middleware. In file app/Http/Middleware/Authenticate.php, make sure your handle method takes a $guard as an argument and that that guard is used when checking if the user is logged in. The final method should look like this:

public function handle($request, Closure $next, $guard = null)
{
   if (Auth::guard($guard)->guest()) {
        if ($request->ajax()) {
            return response('Unauthorized.', 401);
        } else {
            return redirect()->guest('auth/login');
        }
    }

    return $next($request);
}

Laravel

December 10, 2015 11:17

IPv6 addresses should be converted using MySQL's INET6_ATON function. To convert them back to their original value, use INET6_NTOA.

They should be stored as a VARBINARY(16).

This function is only available in MySQL 5.6+

In Laravel, if you're tracking visitors you could do something like this:

$ip = \DB::connection()->getPdo()->quote(request()->ip());

$visitor = Visitor::firstOrCreate([
    'longip' => \DB::raw("INET6_ATON({$ip})")
]);

Laravel, MySQL

November 15, 2015 23:56

CSRF tokens are great for security but there are rare occurrences where the token is invalid when it shouldn't be.

An invalid token is usually a temporary problem* - it's solved immediately on the next page load when their new session is created and token is refreshed. So it's bad UX to simply throw an exception and display a useless page or worse, a nasty error page.

*One case in which it wouldn't be temporary is if sessions aren't working properly at all; for example, if you're using the file session driver but your server is out of disk space or the folder isn't writeable.

A simple solution to this is to catch those exceptions and tell the user to "try again." Now it's just a slight annoyance to your user and not something that will completely stop them in their tracks. Modifying your handler like this takes care of that:

use Illuminate\Session\TokenMismatchException;

class VerifyCsrfToken extends BaseVerifier {
    public function handle($request, Closure $next)
    {
        try {
            return parent::handle($request, $next);
        } catch (TokenMismatchException $e) {
            if ($request->ajax()) {
                return response('CSRF error', 500);
            }

            return redirect()->back()
                ->withInput(\Input::except('_token'))
                ->withErrors(['Something went wrong, please try again']);
        }
    }
}

Laravel