Now I know what you are asking, why do I need a Captcha in Power Apps, and erm, well you don't. But that's not the point, the point is maybe I'm a bit different? well yes but the point is to learn. Developers often have personal projects, most with no point other than to practice their skills, and that's what I like to do.
I haven't made a Power App for a while so I wanted something to make, and while thinking about how to deal with a Captcha on Blue Prism I had a thought, could I make a Captcha in Power Apps.
So that was my challenge, could I come up with 5 totally different ways to beat an RPA bot.
1. The Match Me
This one was inspired by those annoying bike combination locks, if they can beat me half the time surely they can beat the Bot.
I setup up 2 banks of 4 icons, then first bank sets out the required combination, the second is the one you need edit to replicate the first.
OnVisible
Set(vbNew,true);
ClearCollect(colIcons,
{icon:Icon.ArrowDown,id:1},
{icon:Icon.ArrowLeft,id:2},
{icon:Icon.ArrowUp,id:3},
{icon:Icon.ArrowRight,id:4},
{icon:Icon.Blocked,id:5}
);
ClearCollect(colSequence,Shuffle(colIcons));
ClearCollect(colPosition,
{Value:1,id:1,match:false},
{Value:2,id:2,match:false},
{Value:3,id:3,match:false},
{Value:4,id:4,match:false});
The code creates the a collection for our first bank of icons, we then shuffle that collection to make it random (colSequence). The second is a kind of empty collection that we populate when editing the second bank of icons (colPosition).
To edit the icon from the second bank, on each icon I added below code:
OnSelect
If(Index(colPosition,1).Value=CountRows(colIcons),
Set(viNewPosition,1);
,
Set(viNewPosition,Index(colPosition,1).Value+1);
);
Patch(colPostion,{id:1},
{
Value:viNewPosition
}
)
The If part handles looping over from 4 back to 1, then we patch the colPosition with the icons position id.
Then to show the icon we use:
Icon
If(vbNew,
Icon.QuestionMark
,
Index(colIcons,Index(colPostion,1).Value).icon
)
Finally I have a submit button that checks the 2 banks of icons match.
ForAll(Sequence(4),
If(Index(colSequence,ThisRecord.Value).id=Index(colPosition,ThisRecord.Value).Value,
Patch(colPostion,{id:Index(colPosition,ThisRecord.Value).id},
{
match:true
}
)
,
Patch(colPostion,{id:Index(colPostion,ThisRecord.Value).id},
{
match:false
}
)
)
);
If(CountRows(Filter(colPosition, match=true))=CountRows(colPostion),
Notify("You are human",NotificationType.Success,1000)
,
Notify("You are robot",NotificationType.Error,1000)
)
It uses a sequence to loop over the nth id of the colSequence collection matches the nth value of the colPosition collection you edit. if they match we patch the colPosition to match:true.
Finally we count the rows with match=true and check to see it matches the length of colSequence.
2. The Order Me
This one I originally used in a previous project to build a sort order for returned data, the requirement is to order the items into numerical order.
The main component is a gallery, with the collection set on the screen OnVisible:
ClearCollect(colRandom,Shuffle(Sequence(4)));
ClearCollect(colOrder,
{
id:1,
order:Index(colRandom,1).Value,
match:false
},
{
id:2,
order:Index(colRandom,2).Value,
match:false
},
{
id:3,
order:Index(colRandom,3).Value,
match:false
},
{
id:4,
order:Index(colRandom,4).Value,
match:false
}
)
The first collection is the random order, the second is the collection that we reorder. The id is th value shown in the gallery and what we need to get into the right order. The order is how we make the gallery start radominsied. By using the Index we take the 1st from the random array and add to first in the second collection and so on.
The items for the gallery is then put in a random order like this:
Sort(colOrder,order,SortOrder.Ascending)
To change order the up arrow has:
Set(viNewOrder,ThisItem.order-1);
Patch(colOrder,LookUp(colOrder, order=viNewOrder),
{
order:viNewOrder+1
}
);
Patch(colOrder,ThisItem,
{
order:viNewOrder
}
);
and the down arrow:
Set(viNewOrder,ThisItem.order+1);
Patch(colOrder,LookUp(colOrder, order=viNewOrder),
{
order:viNewOrder-1
}
);
Patch(colOrder,ThisItem,
{
order:viNewOrder
}
);
This swaps the order number with the item before/after.
Finally we check the order with the below code OnSelct on the Submit button
ForAll(Sequence(4),
If(Index(Sort(colOrder,order),ThisRecord.Value).order=Index(Sort(colOrder,order),ThisRecord.Value).id,
Patch(colOrder,{id:Index(Sort(colOrder,order),ThisRecord.Value).id},
{
match:true
}
)
,
Patch(colOrder,{id:Index(Sort(colOrder,order),ThisRecord.Value).id},
{
match:false
}
)
)
);
If(CountRows(Filter(colOrder, match=true))=CountRows(colOrder),
Notify("You are human",NotificationType.Success,1000)
,
Notify("You are robot",NotificationType.Error,1000)
)
It loops of the collection and checks to see if the id is the same as the order key. If it is it patches match key to true, if not then patches to false.
Finally count the rows of match true and check it is all of the items in the collection.
3. Follow Me
Confession, this is my favourite one 😎 The objective is to follow the random sequence.
The main component is a gallery set to 3 columns with a simple collection as the items. The Collection also has a key that is patched so that we can turn on the light to follow:
ClearCollect(colFollowSetup,
{
id:1,
on:false
},
{
id:2,
on:false
},
{
id:3,
on:false
},
{
id:4,
on:false
},
{
id:5,
on:false
},
{
id:6,
on:false
},
{
id:7,
on:false
},
{
id:8,
on:false
},
{
id:9,
on:false
}
);
Set(vbTimer,false);
Set(viCurrent,0);
ClearCollect(colRandom,FirstN(Shuffle(Sequence(9)),5));
We set the timer to start false, current 0 which tracks the iterations of the sequence and then a collection for the random sequence to follow.
To start we set the start timer to true to start it and reset the collection we use to track the users presses:
Set(vbTimer,true);
Clear(colFollow);
On the gallery number we have the below OnPress to add the item to the collection recording the users presses:
Collect(colFollow,ThisItem.id)
Next the cool bit, the timer. Every second it increments our iteration, it turns off the previous light and turns on the next from the random sequence using the patch. Then it checks to see if end of sequence to finish.
Set(viCurrent,viCurrent+1);
If(viCurrent>1,
Patch(colFollowSetup,{id:Index(colRandom,viCurrent-1).Value},{
on:false
}
);
);
If(viCurrent>CountRows(colRandom),
Set(viCurrent,0);
Set(vbTimer,false);
,
Patch(colFollowSetup,{id:Index(colRandom,viCurrent).Value},{
on:true
}
)
)
Then we validate it by concatenating the random sequence collection with the users created collection and check to see if they match:
If(Concat(colRandom,Value,",")=Concat(colFollow,Value,","),
Notify("You are human",NotificationType.Success,1000)
,
Notify("You are robot",NotificationType.Error,1000)
)
4. The Write My Colour
One of the things that a bot will struggle with is colours, so this one leverages a random colour. How do we show a random colour, well there is a free API for that.
There are few but the one I'm demoing is dicebear, they provide the ability to create avatar images with a url and query.
Example to get the avatar leo from the adventure series you create image with below url:
https://api.dicebear.com/7.x/adventurer/svg?seed=Leo
And guess what, some of them let you set the colour as a parameter.
So the demo creates an array of colours and selects one at random
ClearCollect(colColours,
{code:"4472C4",colour:"blue",id:1},
{code:"C739B3",colour:"purple",id:2},
{code:"FF0000",colour:"red",id:3},
{code:"ED7D31",colour:"orange",id:4},
{code:"FBFB23",colour:"yellow",id:5},
{code:"00B050",colour:"green",id:6},
{code:"6B4845",colour:"brown",id:7}
);
Set(voColour,Index(colColours,RandBetween(1,CountRows(colColours))));
The image src is then:
"https://api.dicebear.com/7.x/fun-emoji/svg?seed=Bear&backgroundColor="&voColour.code
Finally the validation is very easy, does the impact match the random record (converted to lowercase to make case insensitive):
If(voColour.colour=Lower(inType.Text),
Notify("You are human",NotificationType.Success,1000)
,
Notify("You are robot",NotificationType.Error,1000)
)
5. The Matthew Devaney
And we finally are back to what a Captcha normally is, image recognition.
Again we need a free Image API, and in this case I'm using the Cat API, which is nice enough to give me free cat images (Im sure Matt could create his own with his library 😎)
The plan was to randomly select x cats and y dogs (yep there is a Dog API too) and you select only cats. Unfortunately with the Cat API the random image returned a JSON object not a image url, I was going to look for another API but I ran out of time and ability to look at cats so I had to bodge.
I hardcode some dog urls and found a sequence of cat urls and used them. I then combined the 2 collections, randomised them and returned the first 9:
ClearCollect(colAnimals,
{url:"https://cdn2.thedogapi.com/images/CZfQK3eOk.jpg",type:"dog",id:10},
{url:"https://cdn2.thedogapi.com/images/9NJbNWAW9.jpg",type:"dog",id:11},
{url:"https://cdn2.thedogapi.com/images/B8zP8i5W5.jpg",type:"dog",id:12},
{url:"https://cdn2.thedogapi.com/images/e10KCAlSG.jpg",type:"dog",id:13},
{url:"https://cdn2.thedogapi.com/images/qh7cGcv83.jpg",type:"dog",id:14},
{url:"https://cdn2.thedogapi.com/images/dSE-HKnRP.jpg",type:"dog",id:15},
{url:"https://cdn2.thedogapi.com/images/w4bONqP_O.jpg",type:"dog",id:16},
{url:"https://cdn2.thedogapi.com/images/awSN72EWf.png",type:"dog",id:17},
{url:"https://cdn2.thedogapi.com/images/ssY_amKgW.jpg",type:"dog",id:18}
);
ForAll(Sequence(9),
Collect(colAnimals,
{
url:"https://cdn2.thecatapi.com/images/b0"&ThisRecord.Value&".jpg",
type:"cat",
id:ThisRecord.Value
}
)
);
ClearCollect(colOptions,FirstN(Shuffle(colAnimals),9));
The images are added to a gallery (like the follow me), and then on the image press the image id is saved to another collection. To stop a image being added multiple times I added a piece of logic to check if the collection already contained the item.
If(IsEmpty(Filter(colSelect,id=ThisItem.id)),
Collect(colSelect,ThisItem)
);
The validation step is then to count the 2 collections filtered to cat, if they are the same length then every cat has been selected, if not then the have missed one (that's why we only allow each image once to be selected):
If(
(CountRows(Filter(colOptions,type="cat"))=CountRows(Filter(colSelect,type="cat")))&&
IsEmpty(Filter(colSelect,type="dog"))
,
Notify("You are human",NotificationType.Success,1000)
,
Notify("You are robot",NotificationType.Error,1000)
);
Clear(colSelect);
Hopefully I inspired you to do some random projects, I learned some new techniques, and I'm now thinking this could be a cool interview activity for Power App Developers, so win win.
Solution available to download and look at here
Further Reading
Top comments (5)
You have given wonderful concepts about developing a best captcha option for a business website. I am developing the new variation of theacademicpapers.co.uk/ to optimize the contact and order form. Currently, it is using Google's invisible captcha option but I think that it is not the appropriate way to get rid of spammy messages. The option which you provided, seems the best option to move ahead with as this is not only fency but also will increase the user engagement on the contact form.
Thanks for sharing it with us. I appreciate you. I know that as a student, every student hates to write dissertations; they make me so confused, so to get rid of this problem, a few months ago, I found this ukwritings.com/dissertation-help website online, which offers dissertation writing at a very affordable price. If you are also like me, then you can also use their service for the best results at a very cheap price.
One of the greatest articles I've read ever!!! It's funny, clear, concise, and to the point. And, shows what it has to show.
Congrats!!!!!
Thank you ❤️
Love the dicebear API. for a second i thought you had used AI builder.....
thanks for the inspiring and fun tutorials !!!