DEV Community

Cover image for How to Create a JSON Web Token Using PHP

How to Create a JSON Web Token Using PHP

Rob Waller on January 31, 2018

I have spent the last year intermittently working on a PHP JSON Web Token library called ReallySimpleJWT, and this week I released version 1.0.0. T...
Collapse
 
sagargurnani36 profile image
Sagar Gurnani

I followed your blog topic i.e. "How to Build a JSON Web Token in PHP" in order to generate a JWT token. But, when I try to verify it via the available JWT verifiers (such as jwt.io/) I get the "Invalid Signature" error.

Maybe the checker is buggy. Can you suggest a JWT checker that you use, please? If my token is genuinely invalid, can you suggest some routes to follow so that I can discover what I am doing wrongly?

Collapse
 
robdwaller profile image
Rob Waller

When using jwt.io are you providing them with the correct secret?

Also if you're worried this is an 'issue' with the library feel free to create a ticket with an example token and I'll take a closer look.

github.com/RobDWaller/ReallySimple...

Collapse
 
bobrundle profile image
Bob Rundle

I think this is because the jwt.io checker is a bit counter intuitive. To get the signature to verify you need to paste the secret into the "verify signature" block and also have the "secret base64 encoded" checkbox set properly. If your secret is simply text you leave this check off. If it is binary then you need to base64 encode it before pasting it. Then set this check on.

The JWTs I generated with Rob's code verified fine on jwt.io.

Collapse
 
jeremy66765467 profile image
Jeremy

Hi Rob,
You did a goog job !
I'm pretty new to php development, so i have some questions.
Let's say that I have an ios app, which sends to the server
Username = john doe
Password = helloWorld
Whats going on next ?'
What would be the php code for that in order to create the jwt.
And so, what would be userId, secret, TimedateString and issuer identifier ?
Thksss for what you did Rob !

Collapse
 
robdwaller profile image
Rob Waller

Thanks for the comment, I will try to answer your question.

The username and password, would query the database to check that the user exists. If the user does exist the user identifier for the database table will be retrieved, this is usually an integer. You would then use the identifier to create your token so you know who the user is.

The secret is what you want to hash your token with, it's like a salt, it's for security purposes. The data time string is for the expiry date, when do you want the token to expire, the issue identifier is a reference to the website that generated the token, this could be a URL for example.

I hope that info helps. Let me know if you have any further questions.

Collapse
 
jeremy66765467 profile image
Jeremy

Thanks for your fast answer, i understood more now ! But I still have few questions 🙈
When I want to create my token, I write for example
Token::getToken('24', 'sha256, 1*,2*)
1*) According to you, what is the best dateExpiry ?
2*) When you say that the issue identifier is a reference to the website that generated the token, what do you mean by that ? I thought that was your method who created the toked.
And if the meaning of "generated" is the side that ask for creating the token (i.e my iOS app), is the issue id the bundle id of the App ?
Ps: thanks you a lot, really, and sorry for my misunderstanding
Of words, i'm new to the english too :)

Thread Thread
 
robdwaller profile image
Rob Waller

The expiry should be relatively short, I would say minutes. You should also create a way for you to update tokens as Facebook does. Facebook tokens last for about 60 minutes and if you want to continue making requests after 60 minutes you have to trade the current token for a new token before the current token expires.

The issue identifier is the application that creates the token, not the application or user who asks for the token.

eg

User 1 asks for a token

Website A creates and returns the token to User 1.

In this scenario the issue identifier would be "Website A"

Collapse
 
bcdbuddy profile image
Babacar Cisse DIA • Edited

if you are using public/private key pair you can use the script below

<?php

// Create token header as a JSON string
$header = json_encode(['typ' => 'JWT', 'alg' => 'RS256']);

// Create token payload as a JSON string
$payload = json_encode([
    'exp' => now()->add('day', 30)->timestamp,
    'iss' => 'abc123',
    'sub' => 'something123'
]);


// Encode Header to Base64Url String
$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));

// Encode Payload to Base64Url String
$base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));

$private_key = <<<EOD
-----BEGIN PRIVATE KEY-----
INSERT HERE
-----END PRIVATE KEY-----
EOD;

$signature = "";
$algo = "SHA256";
openssl_sign($base64UrlHeader . "." . $base64UrlPayload, $signature, $private_key, $algo);

// Encode Signature to Base64Url String
$base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));

// Create JWT
$jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;

// Output
//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMjN9.NYlecdiqVuRg0XkWvjFvpLvglmfR1ZT7f8HeDDEoSx8

Enter fullscreen mode Exit fullscreen mode
Collapse
 
zohaib0306 profile image
Zohaib

unable to install pakage on php 5.6.30?

Got an error below.
Problem 1
- paragonie/random_compat v9.99.99 requires php 7 -> your PHP version (5.6.
30) does not satisfy that requirement.
- paragonie/random_compat v9.99.99 requires php 7 -> your PHP version (5.6.
30) does not satisfy that requirement.
- Installation request for paragonie/random_compat (locked at v9.99.99) -> s
atisfiable by paragonie/random_compat[v9.99.99].

Collapse
 
datelligence profile image
datelligence

Thanks Rob, great and unique tutorial for generating web tokens without dependencies, thanks for sharing.

Collapse
 
sallahmed profile image
Mohamed SALL

Hi Rob,

Thanks for this damn great job !
This really help me.

Collapse
 
robdwaller profile image
Rob Waller

Glad it is useful.

Collapse
 
olgertkranga profile image
olgertkranga

Hi Rob! Many thanks. Very very very good!
:)

Collapse
 
dnalevy profile image
dov79

Excellent article, thanks! Helped me build a JWT token in Zoho Deluge for Zoom API.

Only point that seems to be different for Zoom API is this stage should be omitted.

// Encode Signature to Base64Url String

For Zoom the signature is added without further Base64 encryption.

Collapse
 
niemeier23 profile image
niemeier23 • Edited

Hi, Rob. Thanks for the post.

Regarding the secret. How does that get managed on the server-side, between JWT creations and validations? Is it a static config string? Is a new one supposed to be generated for each unique user/session? Or, can I use the same secret (salt) for everything, as long as it's sufficiently complex?

This is something that most JWT articles/explanations omit, and perhaps it's taken for granted that a lot of developers haven't implemented an authorization system before, and therefore aren't sure how not to shoot themselves in the foot, security-wise.

Thanks.

Collapse
 
pmatt1988 profile image
pMatt1988

Hello neimeier. What I have done for the secret is to add it to the user's row in the database. You will use the payload of the jwt to store the username/user id and when the user attempts to authenticate, you can verify the jwt against the secret stored in the users database row. This makes it easier to invalidate tokens as well, since if the user resets their secret in the database, every device connected will have to authenticate again.

Collapse
 
rgo profile image
Ruben

Hi Rob,

Great work! Looks easy to use. However, what's the advantage of your library over something like firebase/php-jwt?

Collapse
 
robdwaller profile image
Rob Waller

Hi, thanks for the response.

I would say my library has a simpler and more eloquent interface. Also the code in my library is more abstracted so should be more robustly tested.

A weakness would be that ReallySimpleJWT hasn't been as widely used therefore in terms of community feedback and integration testing it's probably not as robust.

I would though very much appreciate any feedback you have on my code, feel free to open some issues if you think it necessary.

Cheers, Rob

Collapse
 
kierenccoetzee profile image
Kieren Christopher

Great tut, Rob! Thanks

Collapse
 
souljacker profile image
Gustavo

Rob, if I use your package, how do I get the actual generated JWT to send over a request, for instance?

Collapse
 
mariorusso profile image
Mario Russo

How would you decode this jwt? to extract the information if needed.

Collapse
 
accorinti profile image
accorinti

Hello, ist there a way to use your library in my php scripts without the installer, with the good old php require_once for example?

Collapse
 
robdwaller profile image
Rob Waller

In theory you can download the repo into your own project and require_once all the files you need. Ultimately that is all the Composer autoloader does.

Collapse
 
janirvinfabon profile image
jan__irving

@rob , how can I use the jwt with ES256 algorithm? Thank you.

Collapse
 
robdwaller profile image
Rob Waller

I don't believe I've looked into this yet, if you submit an issue to my repo I can take a look though.

Collapse
 
sujitsingh_24 profile image
Sujit Singh

Hi Rob, how to use JWT in core PHP.

Collapse
 
robdwaller profile image
Rob Waller

I don't believe JWT is built into the core of PHP, someone would need to write an extension.

Collapse
 
tmblog profile image
tmblog

Hi Rob, where to save the JWT in order to post back to a protected page? Anything other than localStorage or cookie?

Collapse
 
hsemix profile image
Hamid Semix

Nice Article

Collapse
 
pars0097 profile image
Mohammad

"if you wish and I will produce a post on validating JWTs in the next week or two"
I liked your post, please start the post on validating JWTs :(
thanks