grant.codes

Jump to menu
  • About
  • Contact
  • Projects
  • Updates
  • More...Likes
  • Photos
  • Galleries
  • Replies
πŸ‘ˆπŸ•ΈπŸ’πŸ‘‰

Liked https://www.vanschneider.com/a-love-letter-to-personal-websites

A love letter to my website - DESK Magazine

Not long ago, the web was still the future. It was a big deal for companies to have their own site, much less individuals. Technology evolved. We picked up a few HTML and CSS tricks, discovered the wonders of Flash. We started spinning up our own sites, complete with guest books and visitors counters.

In those days, our website was our home. An extension of ourselves. Every day we visited our little corner, tweaked it a bit here, adjusted something there, stood back and admired it. Our site was a little piece of the internet we could own.

Fast forward to now and a website almost feels old fashioned. Our social profiles are all-consuming. Curating our Instagram page is our second job. We almost feel an obligation to share our work there, in addition to our personal lives. Our little corner of the internet? It now collects cobwebs.

"Our site was a little piece of the internet we could own."

In contrast to our personal websites, we don't own our social platforms. They own us. On top of eating our time, our emotions and our focus, they are demanding our privacy. Whether we realized it or not, we signed away our rights when we signed up for these platforms. We not only give giant tech companies our personal data – we allow them to use, sell and share our content in whatever way they wish. Soon, we will see the repercussions of freely giving away our data and our work. When it comes to creativity and self-expression, the loss is already apparent.

On social media, we are at the mercy of the platform. It crops our images the way it wants to. It puts our posts in the same, uniform grids. We are yet another profile contained in a platform with a million others, pushed around by the changing tides of a company's whims. Algorithms determine where our posts show up in people’s feeds and in what order, how someone swipes through our photos, where we can and can’t post a link. The company decides whether we're in violation of privacy laws for sharing content we created ourselves. It can ban or shut us down without notice or explanation. On social media, we are not in control.

As designers, we already forfeit a degree of creative control outside of social media. At our day jobs, we usually don’t have a say in the final product. Directors take over. Politics and process force their way in. Clients leave their fingerprints on the work or reject it entirely. If our work does see the light of day, and there's no guarantee, the execution is not always how we imagined it. Work is not the place for personal expression and full creative freedom. It's the place to follow the creative brief and solve the problem presented to us. So what's left for us to call our own?

Our personal website.

We control the layout of our website. We can create a page that reflects our taste, our personality, our style.

We control the narrative, too. It's here we can finally show our work the way it’s intended to be shown. We get to tell the story exactly as we wrote it, with context the audience or user doesn’t typically have. It’s our chance to own our work and put it in its best light.

We decide the way our website functions. We can influence how people interact with our work. We can guide our visitors through our content in the way that most makes sense. We can lead them straight to our contact info.

We choose whether our work stays alive on the internet. As long as we keep our hosting active, our site remains online. Compare that to social media platforms that go public one day and bankrupt the next, shutting down their app and your content along with it.

"Having my own website says I care about what I do beyond clocking in and out and cashing a paycheck."

At the risk of sounding religious about this, and maybe I am, our personal websites are our temples. They remain the one space on the internet where we decide how we are introduced to friends, potential employees and strangers. It’s a place where we can express, on our terms, who we are and what we offer.

As a working professional, it feels empowering to have my own website. Just seeing my personal domain name and my email address that ends in it gives me this little boost of confidence. Scrolling through my work and making small adjustments makes me feel like I’m deciding my future. Considering the percentage of opportunities I get through my portfolio, that feeling is accurate.

Having my own website says I care about what I do beyond clocking in and out and cashing a paycheck. It shows I’m proud of what I create. If my taste or my work or the industry evolves, I have the power to reflect that on my portfolio. If I launch a new project, my first thought is to put it on my homepage. With this blog, I can write articles that connect directly back to me and my website. Social media is a nice way to extend the reach, but it all points back to vanschneider.com. It’s the one link I give to people inquiring about me and my work, rather some www.designplatform.com/vanchneider08247 URL or social media handle I don’t own. My site is the little place I’ve carved out for myself on the world wide web. It’s mine.

Call me old fashioned, call me nostalgic, call this a self-serving attempt to convince you to use Semplice.com. All of those accusations are at least partially accurate. But the real truth is that as long as we’re putting our work in someone else’s hands, we forfeit our ownership over it. When we create our own website, we own it – at least to the extent that the internet, beautiful in its amorphous existence, can be owned.

Posted 6 years ago
Liked 6 years ago

Liked https://www.reddit.com/r/Unexpected/comments/d5rvbd/perfect_landing/?utm_source=ifttt

Perfect landing

Perfect landing from Unexpected
Posted 6 years ago by
uknowme333333
Liked 6 years ago

Liked https://www.reddit.com/r/funny/comments/d5lfrs/i_need_this_movie/?utm_source=ifttt

I need this movie

I need this movie from funny
Posted 6 years ago by
singleladad
Liked 6 years ago

Liked https://www.reddit.com/r/videos/comments/d59tmq/lullaby_for_a_cat/?utm_source=ifttt

Lullaby for a cat

Lullaby for a cat from videos
Posted 6 years ago by
B-lovedWanderer
Liked 6 years ago

Liked https://www.reddit.com/r/funny/comments/d5dn8v/this_is_why_woman_live_longer_than_men/?utm_source=ifttt

This is why woman live longer than men

This is why woman live longer than men from funny
Posted 6 years ago by
MATT280792
Liked 6 years ago

Liked https://www.reddit.com/r/videos/comments/d51soy/when_white_people_say_they_hate_white_people/?utm_source=ifttt

When white people say they hate white people

When white people say they hate white people from videos
Posted 6 years ago by
MrMarsBars
Liked 6 years ago

Liked https://janboddez.tech/notes/463019540

Aaand IndieWeb readers should no longer pick up … Aaand IndieWeb readers should no longer pick up my notes as articles. By Jan Boddez on Sep 16, 2019 Also on Mastodon

Aaand IndieWeb readers should no longer pick up my notes as articles.

Posted 6 years ago by
Jan Boddez
● Also on: mastodon.social
Liked 6 years ago

Liked https://piperswe.me/posts/serverless-indieauth/

Serverless IndieAuth

Lately, I've been enthralled by the IndieWeb movement, and in the process of rebooting my online identity I decided to make my website adhere to as many IndieWeb standards as possible. This started with simple things, like h-cards and h-entrys, which are easy to implement on a static site. But then I got thinking - the static site host I use, Netlify, has a serverless function service, so I might be able to implement the more complex standards.

I decided to start with IndieAuth, because it seemed like the easiest. IndieAuth is a protocol that fulfills a very similar purpose to OpenID. It allows users to authenticate on a web service using their domain name, with the added benefit of also being able to authorize use of standarized protocols on the user's website. The protocol is decently simple: the origin website redirects to the user's authorization URI, that authorization URI asks permission from the user, and the user is redirected back to the origin with a code. That code can be sent back to the user's website to check its validity. These codes usually expire in about 10 minutes.

This whole exchange has some state involved, such as keeping the valid codes around for 10 minutes. The first approach that came to mind was keeping state stored in a Redis cache, which would probably work but would have required a dependency on an external Redis host. I set the project aside for a few hours, and during that time I remembered that JSON web tokens exist.

With JSON web tokens, I can store the state of the exchange on someone else's state storage and not worry about it. A JSON web token is pretty much a payload and a cryptographic signature. This way, my IndieAuth server can provide a JWT with the origin server's client ID and an expiration time, and I don't have to worry about storing the code. When the origin asks to validate the JWT, I can validate the cryptographic signature. That's the essence of how my serverless IndieAuth service works.

There are two Netlify functions called authorize.js and completeAuthorization.js. This code is licensed under CC0, so you can repurpose it to work on your website, and maybe make the error handling a little more... comprehensive. Here's the code for authorize.js:

import * as jwt from 'jsonwebtoken';
import { parse } from 'querystring';
import Negotiator from 'negotiator';
import { hxapp } from '../lambda-import/hxapp';

function htmlEntities(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

export async function handler(event, context, callback) {
try {
if (event.httpMethod === 'GET') {
const { me, client_id, redirect_uri, state, response_type } = event.queryStringParameters;
if (me !== 'https://piperswe.me/' && me !== 'https://piperswe.me') {
return callback(null, {
statusCode: 400,
body: 'Invalid "me" parameter - this endpoint only serves https://piperswe.me.',
});
} else if (response_type != null && response_type !== 'id') {
return callback(null, {
statusCode: 400,
body: 'Invalid "response_type" paremeter - this endpoint only provides identification, not authorization.',
});
} else if (client_id == null) {
return callback(null, {
statusCode: 400,
body: 'Invalid "client_id" parameter - a client ID is required.',
});
} else if (redirect_uri == null) {
return callback(null, {
statusCode: 400,
body: 'Invalid "redirect_uri" parameter - a redirect URI is required.',
});
}

const hxa = await hxapp(client_id, redirect_uri);
console.log(hxa);

return callback(null, {
statusCode: 200,
body: `
<!DOCTYPE html>
<html>
<head>
<title>Authorize</title>
<link rel="stylesheet" href="/main.css" />
</head>
<body>
<div class="container">
<header>
<ul class="nav">
<li>
<a href="${htmlEntities(client_id)}">go back<small>to ${htmlEntities(client_id)}</small></a>
</li>
</ul>
<h1>Authentication</h1>
</header>
${hxa.photo ? `<img style="width: 128px; margin: auto;" src="${htmlEntities(hxa.photo)}" />`
: ''}
<p>${htmlEntities(hxa.name)} would like to verify that you are <a href="https://piperswe.me" rel="me" class="h-card">Piper McCorkle</a>.</p>
<p>${
hxa.verified
? 'The redirect URI has been verified by checking <code>&lt;link rel="redirect_uri"&gt;</code>.'
: '⚠️ The redirect URI has not been verified - <code>&lt;link rel="redirect_uri"&gt;</code> either does not exist or does not match the redirect URI.'
}</p>
<p></p>
<form action="https://piperswe.me/.netlify/functions/completeAuthorization" method="POST" id="form">
<input type="hidden" name="me" value="${htmlEntities(me)}" />
<input type="hidden" name="client_id" value="${htmlEntities(client_id)}" />
<input type="hidden" name="redirect_uri" value="${htmlEntities(redirect_uri)}" />
<input type="hidden" name="state" value="${htmlEntities(state)}" />
<label for="username">Username</label>
<input type="text" id="username" value="pmc" disabled />
<label for="password">Password</label>
<input type="password" name="password" id="password" placeholder="Enter your password then press enter." />
</form>
</div>
<script>
password.onkeydown = function(e) {
if (e.keyCode === 13) form.submit();
};
</script>
</body>
</html>
`
});
} else if (event.httpMethod === 'POST') {
const { code, redirect_uri, client_id } = parse(event.body);
let payload;
try {
payload = jwt.verify(code, process.env.JWT_SECRET);
} catch (e) {
console.log('invalid token');;
return callback(null, {
statusCode: 403,
body: 'Invalid token',
});
}
console.log('valid token');
if (redirect_uri === payload.red && client_id === payload.aud && 'https://piperswe.me/' === payload.sub) {
const negotiator = new Negotiator(event);
switch (negotiator.mediaType([ 'application/json', 'application/x-www-form-urlencoded' ])) {
case 'application/json':
console.log('json');
return callback(null, {
statusCode: 200,
body: JSON.stringify({
me: payload.sub,
}),
});
case 'application/x-www-form-urlencoded':
console.log('form');
return callback(null, {
statusCode: 200,
body: 'me=' + encodeURIComponent(payload.sub),
});
}
} else {
console.log('itrcc');
return callback(null, {
statusCode: 403,
body: 'Invalid token and redirect_uri/client_id combination',
});
}
} else {
throw '';
}
} catch (e) {
console.error(e);
return callback(null, {
statusCode: 405,
body: 'idk what you did but I don\'t like it',
});
}
}

And here's the code for completeAuthorization:

import * as jwt from 'jsonwebtoken';
import * as bcrypt from 'bcryptjs';
import { parse } from 'querystring';

export function handler(event, context, callback) {
const { me, client_id, redirect_uri, state, password } = parse(event.body);
if (me !== 'https://piperswe.me/') {
return callback(null, {
statusCode: 403,
body: 'Invalid me.',
});
}
bcrypt.compare(password, process.env.PASSWORD_HASH, (err, res) => {
if (err) {
return callback(err);
}
if (!res) {
return callback(null, {
statusCode: 403,
body: 'Incorrect password.',
});
}
const token = jwt.sign({
red: redirect_uri,
}, process.env.JWT_SECRET, {
subject: me,
audience: client_id,
expiresIn: '10 minutes',
});
return callback(null, {
statusCode: 302,
headers: {
Location: redirect_uri + '?code=' + encodeURIComponent(token) + '&state=' + encodeURIComponent(state),
},
});
})
}

And finally, here's the code for hxapp.js, which fetches h-x-app data from origin sites:

import cheerio from 'cheerio';
import fetch from 'node-fetch';

export async function hxapp(url, redirect) {
const r = await fetch(url);
const $ = cheerio.load(await r.text());
const hxappel = $('.h-x-app');
const name = hxappel.find('.p-name');
const photo = hxappel.find('.u-photo');
const verified = $('link[rel="redirect_uri"]').attr('href') === redirect;
return {
name: name.text() || url,
photo: photo.text(),
verified,
};
}

If you'd like to respond to this, I don't have WebMentions set up yet (though I might be able to do something with Netlify functions and the GitHub API...), so you'll have to comment on the Lobste.rs post.

Posted 6 years ago by
Liked 6 years ago

Liked https://www.reddit.com/r/Eyebleach/comments/d4zfla/balloon_boys/?utm_source=ifttt

Balloon boys

Balloon boys from Eyebleach
Posted 6 years ago by
KevlarYarmulke
Liked 6 years ago

Liked https://www.reddit.com/r/MadeMeSmile/comments/d49eym/happy_french_girl_and_her_cat_1959/?utm_source=ifttt

Happy French Girl and Her Cat, 1959

Happy French Girl and Her Cat, 1959 from MadeMeSmile
Posted 6 years ago by
Nozomu_Nakazato
Liked 6 years ago
NewerOlder
EmailInstagramGithub

Grant Richmond
grant.codes[email protected]